From 9fe8d0f259f50cd44391bc20592e51cad8d05cd0 Mon Sep 17 00:00:00 2001 From: hikerstk Date: Mon, 12 Aug 2013 23:29:21 +0000 Subject: [PATCH 002/350] Started to work on rewinding. ATM a single kart can be rewound to a previous time (i.e. the race continuous from the previous location). No memory handling is done, ... all work in progress :) git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/rewind@13468 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- sources.cmake | 30 ++- src/input/input_manager.cpp | 21 +- src/items/rubber_ball.cpp | 2 + src/karts/controller/kart_control.hpp | 35 +++ src/karts/kart.cpp | 21 +- src/karts/kart.hpp | 12 +- src/karts/kart_rewinder.cpp | 96 ++++++++ src/karts/kart_rewinder.hpp | 63 +++++ src/main.cpp | 7 +- src/modes/world.cpp | 11 +- src/network/rewind_manager.cpp | 333 ++++++++++++++++++++++++++ src/network/rewind_manager.hpp | 195 +++++++++++++++ src/network/rewinder.cpp | 37 +++ src/network/rewinder.hpp | 68 ++++++ 14 files changed, 904 insertions(+), 27 deletions(-) create mode 100644 src/karts/kart_rewinder.cpp create mode 100644 src/karts/kart_rewinder.hpp create mode 100644 src/network/rewind_manager.cpp create mode 100644 src/network/rewind_manager.hpp create mode 100644 src/network/rewinder.cpp create mode 100644 src/network/rewinder.hpp diff --git a/sources.cmake b/sources.cmake index f6a03a023..1a1a3fc34 100644 --- a/sources.cmake +++ b/sources.cmake @@ -97,8 +97,8 @@ src/items/projectile_manager.cpp src/items/rubber_ball.cpp src/items/rubber_band.cpp src/items/swatter.cpp -src/karts/abstract_kart_animation.cpp src/karts/abstract_kart.cpp +src/karts/abstract_kart_animation.cpp src/karts/cannon_animation.cpp src/karts/controller/ai_base_controller.cpp src/karts/controller/ai_properties.cpp @@ -113,6 +113,7 @@ src/karts/kart_gfx.cpp src/karts/kart_model.cpp src/karts/kart_properties.cpp src/karts/kart_properties_manager.cpp +src/karts/kart_rewinder.cpp src/karts/kart_with_stats.cpp src/karts/max_speed.cpp src/karts/moveable.cpp @@ -144,6 +145,8 @@ src/network/network_manager.cpp src/network/race_info_message.cpp src/network/race_result_message.cpp src/network/race_state.cpp +src/network/rewind_manager.cpp +src/network/rewinder.cpp src/physics/btKart.cpp src/physics/btKartRaycast.cpp src/physics/btUprightConstraint.cpp @@ -187,13 +190,13 @@ src/states_screens/help_screen_4.cpp src/states_screens/kart_selection.cpp src/states_screens/main_menu_screen.cpp src/states_screens/options_screen_audio.cpp -src/states_screens/options_screen_input2.cpp src/states_screens/options_screen_input.cpp +src/states_screens/options_screen_input2.cpp src/states_screens/options_screen_players.cpp src/states_screens/options_screen_ui.cpp src/states_screens/options_screen_video.cpp -src/states_screens/race_gui_base.cpp src/states_screens/race_gui.cpp +src/states_screens/race_gui_base.cpp src/states_screens/race_gui_overworld.cpp src/states_screens/race_result_gui.cpp src/states_screens/race_setup_screen.cpp @@ -253,8 +256,8 @@ src/animations/animation_base.hpp src/animations/ipo.hpp src/animations/three_d_animation.hpp src/audio/dummy_sfx.hpp -src/audio/music_dummy.hpp src/audio/music.hpp +src/audio/music_dummy.hpp src/audio/music_information.hpp src/audio/music_manager.hpp src/audio/music_ogg.hpp @@ -262,8 +265,8 @@ src/audio/sfx_base.hpp src/audio/sfx_buffer.hpp src/audio/sfx_manager.hpp src/audio/sfx_openal.hpp -src/challenges/challenge_data.hpp src/challenges/challenge.hpp +src/challenges/challenge_data.hpp src/challenges/game_slot.hpp src/challenges/unlock_manager.hpp src/config/device_config.hpp @@ -305,11 +308,11 @@ src/guiengine/scalable_font.hpp src/guiengine/screen.hpp src/guiengine/skin.hpp src/guiengine/widget.hpp +src/guiengine/widgets.hpp src/guiengine/widgets/bubble_widget.hpp src/guiengine/widgets/button_widget.hpp src/guiengine/widgets/check_box_widget.hpp src/guiengine/widgets/dynamic_ribbon_widget.hpp -src/guiengine/widgets.hpp src/guiengine/widgets/icon_button_widget.hpp src/guiengine/widgets/label_widget.hpp src/guiengine/widgets/list_widget.hpp @@ -321,8 +324,8 @@ src/guiengine/widgets/spinner_widget.hpp src/guiengine/widgets/text_box_widget.hpp src/input/binding.hpp src/input/device_manager.hpp -src/input/input_device.hpp src/input/input.hpp +src/input/input_device.hpp src/input/input_manager.hpp src/input/wiimote.hpp src/input/wiimote_manager.hpp @@ -344,8 +347,8 @@ src/items/projectile_manager.hpp src/items/rubber_ball.hpp src/items/rubber_band.hpp src/items/swatter.hpp -src/karts/abstract_kart_animation.hpp src/karts/abstract_kart.hpp +src/karts/abstract_kart_animation.hpp src/karts/cannon_animation.hpp src/karts/controller/ai_base_controller.hpp src/karts/controller/ai_properties.hpp @@ -356,11 +359,12 @@ src/karts/controller/player_controller.hpp src/karts/controller/skidding_ai.hpp src/karts/explosion_animation.hpp src/karts/ghost_kart.hpp -src/karts/kart_gfx.hpp src/karts/kart.hpp +src/karts/kart_gfx.hpp src/karts/kart_model.hpp src/karts/kart_properties.hpp src/karts/kart_properties_manager.hpp +src/karts/kart_rewinder.hpp src/karts/kart_with_stats.hpp src/karts/max_speed.hpp src/karts/moveable.hpp @@ -400,6 +404,8 @@ src/network/race_result_message.hpp src/network/race_start_message.hpp src/network/race_state.hpp src/network/remote_kart_info.hpp +src/network/rewind_manager.hpp +src/network/rewinder.hpp src/network/world_loaded_message.hpp src/physics/btKart.hpp src/physics/btKartRaycast.hpp @@ -447,13 +453,13 @@ src/states_screens/help_screen_4.hpp src/states_screens/kart_selection.hpp src/states_screens/main_menu_screen.hpp src/states_screens/options_screen_audio.hpp -src/states_screens/options_screen_input2.hpp src/states_screens/options_screen_input.hpp +src/states_screens/options_screen_input2.hpp src/states_screens/options_screen_players.hpp src/states_screens/options_screen_ui.hpp src/states_screens/options_screen_video.hpp -src/states_screens/race_gui_base.hpp src/states_screens/race_gui.hpp +src/states_screens/race_gui_base.hpp src/states_screens/race_gui_overworld.hpp src/states_screens/race_result_gui.hpp src/states_screens/race_setup_screen.hpp @@ -483,8 +489,8 @@ src/tracks/check_sphere.hpp src/tracks/check_structure.hpp src/tracks/graph_node.hpp src/tracks/lod_node_loader.hpp -src/tracks/quad_graph.hpp src/tracks/quad.hpp +src/tracks/quad_graph.hpp src/tracks/quad_set.hpp src/tracks/terrain_info.hpp src/tracks/track.hpp diff --git a/src/input/input_manager.cpp b/src/input/input_manager.cpp index 7ac13e5e0..296f53b28 100644 --- a/src/input/input_manager.cpp +++ b/src/input/input_manager.cpp @@ -28,6 +28,7 @@ #include "modes/demo_world.hpp" #include "modes/profile_world.hpp" #include "modes/world.hpp" +#include "network/rewind_manager.hpp" #include "physics/physics.hpp" #include "race/history.hpp" #include "replay/replay_recorder.hpp" @@ -92,6 +93,8 @@ InputManager::~InputManager() void InputManager::handleStaticAction(int key, int value) { static bool control_is_pressed = false; + static bool shift_is_pressed = false; + World *world = World::getWorld(); // When no players... a cutscene @@ -131,7 +134,10 @@ void InputManager::handleStaticAction(int key, int value) case KEY_LWIN: control_is_pressed = value!=0; break; - + case KEY_LSHIFT: + case KEY_RSHIFT: + case KEY_SHIFT: + shift_is_pressed = value!=0; break; case KEY_KEY_I: { if (!world || !UserConfigParams::m_artist_debug_mode) break; @@ -161,7 +167,18 @@ void InputManager::handleStaticAction(int key, int value) break; case KEY_F1: - if (UserConfigParams::m_artist_debug_mode && world) + if(value && shift_is_pressed && world && RewindManager::isEnabled()) + { + printf("Enter rewind time:"); + char s[256]; + fgets(s, 256, stdin); + float t; + StringUtils::fromString(s,t); + RewindManager::get()->rewindTo(world->getTime()-t); + Log::info("Rewind", "Rewinding from %f to %f", + world->getTime(), world->getTime()-t); + } + else if (UserConfigParams::m_artist_debug_mode && world) { AbstractKart* kart = world->getLocalPlayerKart(0); diff --git a/src/items/rubber_ball.cpp b/src/items/rubber_ball.cpp index 8d133d12c..ffef9e407 100644 --- a/src/items/rubber_ball.cpp +++ b/src/items/rubber_ball.cpp @@ -422,6 +422,8 @@ void RubberBall::moveTowardsTarget(Vec3 *next_xyz, float dt) // at it directly, stop interpolating, instead fly straight // towards it. Vec3 diff = m_target->getXYZ()-getXYZ(); + if(diff.length()==0) + printf("diff=0\n"); *next_xyz = getXYZ() + (dt*m_speed/diff.length())*diff; Vec3 old_vec = getXYZ()-m_previous_xyz; diff --git a/src/karts/controller/kart_control.hpp b/src/karts/controller/kart_control.hpp index 78f789e7c..f5a5ba680 100644 --- a/src/karts/controller/kart_control.hpp +++ b/src/karts/controller/kart_control.hpp @@ -21,6 +21,8 @@ #include "network/message.hpp" +#include + /** * \ingroup controller */ @@ -73,6 +75,21 @@ public: m_fire = false; m_look_back = false; } // reset + // ------------------------------------------------------------------------ + /** Tests if two KartControls are equal. + */ + bool operator==(const KartControl &other) + { + return m_steer == other.m_steer && + m_accel == other.m_accel && + m_brake == other.m_brake && + m_nitro == other.m_nitro && + m_skid == other.m_skid && + m_rescue == other.m_rescue && + m_fire == other.m_fire && + m_look_back == other.m_look_back; + } // operator== + // ------------------------------------------------------------------------ /** Return the serialised size in bytes. */ static int getLength() { return 9; } @@ -84,6 +101,24 @@ public: m->addFloat(m_accel); m->addChar(getButtonsCompressed()); } // compress + // ------------------------------------------------------------------------ + /** Copies the important data from this objects into a memory buffer. */ + void copyToMemory(char *buffer) + { + memcpy(buffer, &m_steer, sizeof(float)); + memcpy(buffer+sizeof(float), &m_accel, sizeof(float)); + buffer[2*sizeof(float)] = getButtonsCompressed(); + } // copyToMemory + + // ------------------------------------------------------------------------ + /** Restores this object from a previously saved memory buffer. */ + void setFromMemory(char *buffer) + { + memcpy(&m_steer, buffer, sizeof(float)); + memcpy(&m_accel, buffer+4*sizeof(float), sizeof(float)); + setButtonsCompressed(buffer[2*sizeof(float)]); + } // setFromMemory + // ------------------------------------------------------------------------ void uncompress(char *c) { diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 1003871d7..60ac56178 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -19,13 +19,6 @@ #include "karts/kart.hpp" -#include -#include -#include // for min and max - -#include -#include - #include "audio/music_manager.hpp" #include "audio/sfx_manager.hpp" #include "audio/sfx_base.hpp" @@ -44,6 +37,7 @@ #include "guiengine/scalable_font.hpp" #include "karts/explosion_animation.hpp" #include "karts/kart_gfx.hpp" +#include "karts/kart_rewinder.hpp" #include "karts/rescue_animation.hpp" #include "modes/overworld.hpp" #include "modes/world.hpp" @@ -71,6 +65,13 @@ #include "utils/log.hpp" //TODO: remove after debugging is done +#include +#include +#include // for min and max + +#include +#include + #if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) // Disable warning for using 'this' in base member initializer list @@ -220,6 +221,8 @@ void Kart::init(RaceManager::KartType type) 0.0f) ); reset(); + + m_rewinder = new KartRewinder(this); } // init // ---------------------------------------------------------------------------- @@ -229,6 +232,8 @@ void Kart::init(RaceManager::KartType type) */ Kart::~Kart() { + delete m_rewinder; + // Delete all custom sounds (TODO: add back when properly done) /* for (int n = 0; n < SFXManager::NUM_CUSTOMS; n++) @@ -1055,6 +1060,8 @@ void Kart::eliminate() */ void Kart::update(float dt) { + m_rewinder->update(); + if ( UserConfigParams::m_graphical_effects ) { // update star effect (call will do nothing if stars are not activated) diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index e81a14708..197e5fbfb 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -34,15 +34,16 @@ #include "tracks/terrain_info.hpp" #include "utils/no_copy.hpp" + +class AbstractKartAnimation; +class Attachment; class btKart; class btUprightConstraint; - -class Attachment; class Controller; -class Item; -class AbstractKartAnimation; class HitEffect; +class Item; class KartGFX; +class KartRewinder; class MaxSpeed; class ParticleEmitter; class ParticleKind; @@ -198,6 +199,9 @@ private: float m_view_blocked_by_plunger; float m_speed; + /** The rewinder object for network play. */ + KartRewinder *m_rewinder; + std::vector m_custom_sounds; SFXBase *m_beep_sound; SFXBase *m_engine_sound; diff --git a/src/karts/kart_rewinder.cpp b/src/karts/kart_rewinder.cpp new file mode 100644 index 000000000..08fc380b2 --- /dev/null +++ b/src/karts/kart_rewinder.cpp @@ -0,0 +1,96 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Joerg Henrichs +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "karts/kart_rewinder.hpp" +#include "karts/abstract_kart.hpp" +#include "modes/world.hpp" +#include "network/rewind_manager.hpp" +#include "utils/vec3.hpp" + +#include + +KartRewinder::KartRewinder(AbstractKart *kart) : Rewinder(/*can_be_destroyed*/ false) +{ + m_kart = kart; +} // KartRewinder + +// ---------------------------------------------------------------------------- +/** Saves all state information for a kart in a memory buffer. The memory + * is allocated here and the address returned. It will then be managed + * by the RewindManager. The size is used to keep track of memory usage + * for rewinding. + * \param[out] buffer Address of the memory buffer. + * \returns Size of allocated memory, or -1 in case of an error. + */ +int KartRewinder::getState(char **buffer) const +{ + const int MEMSIZE = 13*sizeof(float); + + *buffer = new char[MEMSIZE]; + float* p = (float*)*buffer; + if(!buffer) + { + Log::error("KartRewinder", "Can not allocate %d bytes.", MEMSIZE); + return -1; + } + + const btRigidBody *body = m_kart->getBody(); + + const btTransform &t = body->getWorldTransform(); + btQuaternion q = t.getRotation(); + memcpy(p+ 0, t.getOrigin(), 3*sizeof(float)); + memcpy(p+ 3, &q, 4*sizeof(float)); + memcpy(p+ 7, body->getLinearVelocity(), 3*sizeof(float)); + memcpy(p+10, body->getAngularVelocity(), 3*sizeof(float)); + return MEMSIZE; +} // getState + +// ---------------------------------------------------------------------------- +/** Called once a frame. It will add a new kart control event to the rewind + * manager if any control values have changed. + */ +void KartRewinder::update() +{ + if(m_kart->getControls() == m_previous_control) + return; + m_previous_control = m_kart->getControls(); + + char *buffer = new char[m_previous_control.getLength()]; + m_previous_control.copyToMemory(buffer); + + // The rewind manager will free the memory once it's not needed anymore + RewindManager::get()->addEvent(this, World::getWorld()->getTime(), buffer); +} // update + +// ---------------------------------------------------------------------------- +/** Actuall rewind to the specified state. */ +void KartRewinder::rewindToState(char *buffer) +{ + btTransform t; + float *p = (float*)buffer; + t.setOrigin(*(btVector3*)p); + t.setRotation(*(btQuaternion*)(p+3)); + btRigidBody *body = m_kart->getBody(); + body->proceedToTransform(t); + body->setLinearVelocity(*(btVector3*)(p+7)); + body->setAngularVelocity(*(btVector3*)(p+10)); + + return; +} // rewindToState + + diff --git a/src/karts/kart_rewinder.hpp b/src/karts/kart_rewinder.hpp new file mode 100644 index 000000000..806384261 --- /dev/null +++ b/src/karts/kart_rewinder.hpp @@ -0,0 +1,63 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Joerg Henrichs +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_KART_REWINDER_HPP +#define HEADER_KART_REWINDER_HPP + +#include "karts/controller/kart_control.hpp" + +#include "network/rewinder.hpp" +#include "utils/cpp2011.h" + +class AbstractKart; + +class KartRewinder : public Rewinder +{ +private: + /** Pointer to the original kart object. */ + AbstractKart *m_kart; + + KartControl m_previous_control; + +public: + KartRewinder(AbstractKart *kart); + virtual ~KartRewinder() {}; + virtual int getState(char **buffer) const; + virtual void rewindToState(char *p) OVERRIDE; + virtual void rewindToEvent(char *p) OVERRIDE + { + }; // rewindToEvent + + // ------------------------------------------------------------------------- + virtual void undoState(char *p) OVERRIDE + { + }; // undoState + + // ------------------------------------------------------------------------- + virtual void undoEvent(char *p) OVERRIDE + { + }; // undoEvent + + // ------------------------------------------------------------------------- + + void update(); + + +}; // Rewinder +#endif + diff --git a/src/main.cpp b/src/main.cpp index 88da24f5c..cbb965f1e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -168,6 +168,7 @@ #include "modes/demo_world.hpp" #include "modes/profile_world.hpp" #include "network/network_manager.hpp" +#include "network/rewind_manager.hpp" #include "race/grand_prix_manager.hpp" #include "race/highscore_manager.hpp" #include "race/history.hpp" @@ -590,7 +591,7 @@ int handleCmdLinePreliminary(int argc, char **argv) } // --verbose or -v } return 0; -} +} // handleCmdLinePreliminary // ============================================================================ /** Handles command line options. @@ -638,6 +639,10 @@ int handleCmdLine(int argc, char **argv) { UserConfigParams::m_camera_debug=1; } + else if( !strcmp(argv[i], "--rewind") ) + { + RewindManager::setEnable(true); + } else if(UserConfigParams::m_artist_debug_mode && !strcmp(argv[i], "--physics-debug")) { diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 92e76e05f..5d40e5239 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -44,6 +44,7 @@ #include "modes/profile_world.hpp" #include "network/network_manager.hpp" #include "network/race_state.hpp" +#include "network/rewind_manager.hpp" #include "physics/btKart.hpp" #include "physics/physics.hpp" #include "physics/triangle_mesh.hpp" @@ -148,6 +149,8 @@ void World::init() // constructor is called, so the wrong race gui would be created. createRaceGUI(); + RewindManager::create(); + // Grab the track file m_track = track_manager->getTrack(race_manager->getTrackName()); if(!m_track) @@ -201,6 +204,8 @@ void World::init() */ void World::reset() { + RewindManager::get()->reset(); + // If m_saved_race_gui is set, it means that the restart was done // when the race result gui was being shown. In this case restore the // race gui (note that the race result gui is cached and so never really @@ -346,6 +351,8 @@ Controller* World::loadAIController(AbstractKart *kart) //----------------------------------------------------------------------------- World::~World() { + RewindManager::destroy(); + if(ReplayPlay::get()) { // Destroy the old replay object, which also stored the ghost @@ -844,7 +851,9 @@ void World::update(float dt) projectile_manager->update(dt); - PROFILER_POP_CPU_MARKER(); + RewindManager::get()->update(dt); + + PROFILER_POP_CPU_MARKER(); #ifdef DEBUG assert(m_magic_number == 0xB01D6543); diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp new file mode 100644 index 000000000..166c1e991 --- /dev/null +++ b/src/network/rewind_manager.cpp @@ -0,0 +1,333 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Joerg Henrichs +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/rewind_manager.hpp" + +#include "modes/world.hpp" +#include "network/rewinder.hpp" +#include "utils/log.hpp" + +RewindManager* RewindManager::m_rewind_manager = NULL; +bool RewindManager::m_enable_rewind_manager = false; + +/** Creates the singleton. */ +RewindManager *RewindManager::create() +{ + assert(!m_rewind_manager); + m_rewind_manager = new RewindManager(); + return m_rewind_manager; +} // create + +// ---------------------------------------------------------------------------- +/** Destroys the singleton. */ +void RewindManager::destroy() +{ + assert(m_rewind_manager); + delete m_rewind_manager; + m_rewind_manager = NULL; +} // destroy + +// ============================================================================ +/** Constructor for a state: it only takes the size, and allocates a buffer + * for all state info. + * \param size Necessary buffer size for a state. + */ +RewindManager::RewindInfo::RewindInfo(Rewinder *rewinder, float time, + char *buffer, bool is_event, + bool is_confirmed) +{ + m_rewinder = rewinder; + m_time = time; + m_buffer = buffer; + m_is_event = is_event; + m_is_confirmed = is_confirmed; +} // RewindInfo + +// ============================================================================ +/** The constructor. + */ +RewindManager::RewindManager() +{ + reset(); +} // RewindManager + +// ---------------------------------------------------------------------------- +/** Frees all saved state information. Note that the Rewinder data must be + * freed elsewhere. + */ +RewindManager::~RewindManager() +{ + // Destroying the + for(unsigned int i=0; icanBeDestroyed()) + { + r++; + continue; + } + Rewinder *rewinder = *r; + r = m_all_rewinder.erase(r); + // FIXME Do we really want to delete this here? + delete rewinder; + } + + for(unsigned int i=0; igetTime(); + + if(ri->isEvent()) + { + // If there are several infos for the same time t, + // events must be inserted at the end + AllRewindInfo::reverse_iterator i = m_rewind_info.rbegin(); + while(i!=m_rewind_info.rend() && + (*i)->getTime() > t) + { +#ifdef REWIND_SEARCH_STATS + m_count_of_comparisons++; +#endif + i++; + } + AllRewindInfo::iterator insert_point = i.base(); + m_rewind_info.insert(insert_point,ri); + return; + + } + else // is a states + { + // If there are several infos for the same time t, + // a state must be inserted first + AllRewindInfo::reverse_iterator i = m_rewind_info.rbegin(); + while(i!=m_rewind_info.rend() && (*i)->getTime() >= t) + { +#ifdef REWIND_SEARCH_STATS + m_count_of_comparisons++; +#endif + i++; + } + AllRewindInfo::iterator insert_point = i.base(); + m_rewind_info.insert(insert_point,ri); + return; + } +} // insertRewindData + +// ---------------------------------------------------------------------------- +/** Returns the first (i.e. lowest) index i in m_rewind_info which fulfills + * time(i) < target_time <= time(i+1) + * This is used to determine the starting point from which to rewind. + * \param time Time for which an index is searched. + * \return Index in m_rewind_info after which to add rewind data. + */ +unsigned int RewindManager::findFirstIndex(float target_time) const +{ + // For now do a linear search, even though m_rewind_info is sorted + // I would expect that most insertions will be towards the (very) + // end of the list. Note that after finding an entry in a binary + // search, you stil have to do a linear search to find the last + // entry with the same time in order to minimise the later + // necessary memory move. + + // Gather some statistics about search for now: +#ifdef REWIND_SEARCH_STATS + m_count_of_searches++; +#endif + int index = m_rewind_info.size()-1; + while(index>=0) + { +#ifdef REWIND_SEARCH_STATS + m_count_of_comparisons++; +#endif + if(m_rewind_info[index]->getTime()getTime(), target_time); + return 0; // avoid compiler warning +} // findFirstIndex + +// ---------------------------------------------------------------------------- +/** Adds an event to the rewind data. The data to be stored must be allocated + * and not freed by the caller! + * \param time Time at which the event was recorded. + * \param buffer Pointer to the event data. + */ +void RewindManager::addEvent(Rewinder *rewinder, float time, char *buffer) +{ + RewindInfo *ri = new RewindInfo(rewinder, time, buffer, /*is_event*/true, + /*is_confirmed*/true); + insertRewindInfo(ri); +} // addEvent + +// ---------------------------------------------------------------------------- +/** Determines if a new state snapshot should be taken, and if so calls all + * rewinder to do so. + * \param dt Time step size. + */ +void RewindManager::update(float dt) +{ + if(!m_enable_rewind_manager || m_all_rewinder.size()==0) return; + + float time = World::getWorld()->getTime(); + + // For now always create a snapshot. + for(unsigned int i=0; igetState(&p); + if(size>=0) + { + m_overall_state_size += size; + RewindInfo *ri = new RewindInfo(m_all_rewinder[i], time, p, + /*is_event*/false, + /*is_confirmed*/true); + assert(ri); + insertRewindInfo(ri); + } // size >= 0 + } + + Log::verbose("RewindManager", "%f allocated %ld bytes search %d/%d=%f", + World::getWorld()->getTime(), m_overall_state_size, + m_count_of_comparisons, m_count_of_searches, + float(m_count_of_comparisons)/ float(m_count_of_searches) ); + +} // update +// ---------------------------------------------------------------------------- +/** Rewinds to the specified time. + * \param t Time to rewind to. + */ +void RewindManager::rewindTo(float rewind_time) +{ + // First find the state to which we need to rewind + // ------------------------------------------------ + int state = findFirstIndex(rewind_time); + + if(m_rewind_info[state]->isEvent()) + { + Log::error("RewindManager", "No state for rewind to %d, state %d.", + rewind_time, state); + return; + } + + // Then undo the states that are skipped + // ------------------------------------- + for(int i=m_rewind_info.size()-1; i>(int)state; i--) + { + m_rewind_info[i]->undo(); + } // for i>state + + + // TODO: we need some logic here to handle that confirmed states are not + // necessarily the same for each rewinder. So if we rewind to t, one + // rewinder forces us to go back to t1 (latest state saved before t), + // another one to t2. + // So we have to detect the latest time t_min < t for which each + // rewinder has a confirmed state. Then we rewind from t_min. But if we + // find another confirmed state for a rewinder at time t1 (t_mingetTime(); + + // Rewind all objects (and also all state) that happen at the + // current time. + // TODO: this assumes atm that all rewinder have a initial state + // for 'current_time'!!! + while(m_rewind_info[state]->getTime()==current_time) + { + m_rewind_info[state]->rewind(); + state ++; + } // while rewind info at time current_time + + // Store the time to which we have to replay to + float current_time = World::getWorld()->getTime(); + + World::setTime(current_time); + + // Now go forward through the saved states, and + // replay taking the events into account. + while(World::getWorld()->getTime() < current_time) + { + + // Find the next event which needs to be taken into account + // All other states can be deleted + // TODO ... for now + while(state < m_rewind_info.size() && !m_rewind_info[state]->isEvent()) + state ++; + float dt = determineTimeStepSize(state); + World::getWorld()->update(dt); + + } + +} // rewindTo + +// ---------------------------------------------------------------------------- +/** Determines the next time step size to use when recomputing the physics. + * The time step size is either 1/60 (default physics), or less, if there + * is an even to handle before that time. + * \param state The next state to replay. + * \return The time step size to use in the next simulation step. + */ +float RewindManager::determineTimeStepSize(int state) +{ + float dt = 1.0f/60.0f; + float t = World::getWorld()->getTime(); + if(m_rewind_info[state]->getTime() < t + dt) + return t-m_rewind_info[state]->getTime(); + return dt; +} // determineTimeStepSize diff --git a/src/network/rewind_manager.hpp b/src/network/rewind_manager.hpp new file mode 100644 index 000000000..72d5647e1 --- /dev/null +++ b/src/network/rewind_manager.hpp @@ -0,0 +1,195 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Joerg Henrichs +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_REWIND_MANAGER_HPP +#define HEADER_REWIND_MANAGER_HPP + +#include "network/rewinder.hpp" +#include "utils/ptr_vector.hpp" + +#include +#include + + +class RewindManager +{ +private: + /** Singleton pointer. */ + static RewindManager *m_rewind_manager; + + /** En- or Disable the rewind manager. This is used to disable storing + * rewind data in case of local races only. */ + static bool m_enable_rewind_manager; + + typedef std::vector AllRewinder; + + /** A list of all objects that can be rewound. */ + AllRewinder m_all_rewinder; + + /** Overall amount of memory allocated by states. */ + unsigned int m_overall_state_size; + + // ======================================================================== + /** Used to store rewind information for a given time for all rewind + * instances. + * Rewind information can either be a state (for example a kart would + * have position, rotation, linear and angular velocity, ... as state), + * or an event (for a kart that would be pressing or releasing of a key). + * State changes and events can be delivered in different frequencies, + * and might be released (to save memory) differently: A state can be + * reproduced from a previous state by replaying the simulation taking + * all events into account. + */ + class RewindInfo + { + private: + /** Pointer to the buffer which stores all states. */ + char *m_buffer; + + /** Time when this state was taken. */ + float m_time; + + /** True if this is an event, and not a state. */ + bool m_is_event; + + /** A confirmed event is one that was sent from the server. When + * rewinding we have to start with a confirmed state for each + * object. */ + bool m_is_confirmed; + + /** The Rewinder instance for which this data is. */ + Rewinder *m_rewinder; + public: + RewindInfo(Rewinder *rewinder, float time, char *buffer, + bool is_event, bool is_confirmed); + // -------------------------------------------------------------------- + ~RewindInfo() + { + delete m_buffer; + } // ~RewindInfo + // -------------------------------------------------------------------- + /** Returns a pointer to the state buffer. */ + char *getBuffer() const { return m_buffer; } + // -------------------------------------------------------------------- + /** Returns the time at which this rewind state was saved. */ + float getTime() const { return m_time; } + // -------------------------------------------------------------------- + bool isEvent() const { return m_is_event; } + // -------------------------------------------------------------------- + /** Returns if this state is confirmed. */ + bool isConfirmed() const { return m_is_confirmed; } + // -------------------------------------------------------------------- + /** Called when going back in time to undo any rewind information. + * It calls either undoEvent or undoState in the rewinder. */ + void undo() + { + if(m_is_event) + m_rewinder->undoEvent(m_buffer); + else + m_rewinder->undoState(m_buffer); + } // undoEvent + // -------------------------------------------------------------------- + /** Rewinds to this state. This is called while going forwards in time + * again to reach current time. If the info is a state, it will + * call rewindToState(char *) if the state is a confirmed state, or + * rewindReplace(char*) in order to discard the old stored data, + * and replace it with the new state at that time. In case of an + * event, rewindEvent(char*) is called. + */ + void rewind() + { + if(m_is_event) + m_rewinder->rewindToEvent(m_buffer); + else + { + if(m_is_confirmed) + m_rewinder->rewindToState(m_buffer); + else + { + // TODO + // Handle replacing of stored states. + } + } + } // rewind + }; // RewindInfo + // ======================================================================== + + /** Pointer to all saved states. */ + typedef std::vector AllRewindInfo; + + AllRewindInfo m_rewind_info; + +#define REWIND_SEARCH_STATS + +#ifdef REWIND_SEARCH_STATS + /** Gather some statistics about how many comparisons we do, + * to find out if it's worth doing a binary search.*/ + mutable int m_count_of_comparisons; + mutable int m_count_of_searches; +#endif + + RewindManager(); + ~RewindManager(); + unsigned int findFirstIndex(float time) const; + void insertRewindInfo(RewindInfo *ri); + +public: + // First static functions to manage rewinding. + // =========================================== + static RewindManager *create(); + static void destroy(); + // ------------------------------------------------------------------------ + /** En- or disables rewinding. */ + static void setEnable(bool m) { m_enable_rewind_manager = m;} + + // ------------------------------------------------------------------------ + /** Returns if rewinding is enabled or not. */ + static bool isEnabled() { return m_enable_rewind_manager; } + + // ------------------------------------------------------------------------ + /** Returns the singleton. This function will not automatically create + * the singleton. */ + static RewindManager *get() + { + assert(m_rewind_manager); + return m_rewind_manager; + } // get + + // ------------------------------------------------------------------------ + + void reset(); + void update(float dt); + // ------------------------------------------------------------------------ + /** Adds a Rewinder to the list of all rewinders. + * \return true If rewinding is enabled, false otherwise. + */ + bool addRewinder(Rewinder *rewinder) + { + if(!m_enable_rewind_manager) return false; + m_all_rewinder.push_back(rewinder); + return true; + } // addRewinder + // ------------------------------------------------------------------------ + void rewindTo(float target_time); + // ------------------------------------------------------------------------ + void addEvent(Rewinder *rewinder, float time, char *buffer); +}; // RewindManager + + +#endif + diff --git a/src/network/rewinder.cpp b/src/network/rewinder.cpp new file mode 100644 index 000000000..91b808a78 --- /dev/null +++ b/src/network/rewinder.cpp @@ -0,0 +1,37 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Joerg Henrichs +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/rewinder.hpp" + +#include "network/rewind_manager.hpp" + +/** Constructor. It will add this object to the list of all rewindable + * objects in the rewind manager. + */ +Rewinder::Rewinder(bool can_be_destroyed) +{ + m_can_be_destroyed = can_be_destroyed; + RewindManager::get()->addRewinder(this); +} // Rewinder + +// ---------------------------------------------------------------------------- +/** Destructor. + */ +Rewinder::~Rewinder() +{ +} // ~Rewinder diff --git a/src/network/rewinder.hpp b/src/network/rewinder.hpp new file mode 100644 index 000000000..f30936181 --- /dev/null +++ b/src/network/rewinder.hpp @@ -0,0 +1,68 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Joerg Henrichs +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_REWINDER_HPP +#define HEADER_REWINDER_HPP + +class Rewinder +{ +private: + bool m_can_be_destroyed; +public: + Rewinder(bool can_be_destroyed); + virtual ~Rewinder(); + + /** Provides a copy of the state of the object in one memory buffer. + * The memory is managed by the RewindManager. + * \param[out] buffer The address of the memory buffer with the state. + * \return Size of the buffer, or -1 in case of an error. + */ + virtual int getState(char **buffer) const = 0; + + /** Called when an event needs to be undone. This is called while going + * backwards for rewinding - all stored events will get an 'undo' call. + * A dummy implementation is provided which just ignores this. + */ + virtual void undoEvent(char *buffer) = 0; + + /** Called when an event needs to be replayed. This is called during + * rewind, i.e. when going forward in time again. + */ + virtual void rewindToEvent(char *buffer) = 0; + + /** Called when a state needs to be replayed. This is called during + * rewind, i.e. when going forward in time again, and only for confirmed + * states. + */ + virtual void rewindToState(char *buffer) = 0; + + /** Undo the effects of the given state, but do not rewind to that + * state (which is done by rewindTo). This is called while going + * backwards for rewinding - all stored events will get an 'undo' call. + * Provided here a dummy implementation that just ignores the state. + */ + virtual void undoState(char *p) = 0; + + // ------------------------------------------------------------------------- + /** True if this rewinder can be destroyed. Karts can not be destroyed, + * cakes can. This is used by the RewindManager in reset. */ + bool canBeDestroyed() const { return m_can_be_destroyed; } + +}; // Rewinder +#endif + From 47b170ffb3a6cf7d87a71345f64d03d35a54ff5d Mon Sep 17 00:00:00 2001 From: hikerstk Date: Tue, 13 Aug 2013 00:02:51 +0000 Subject: [PATCH 003/350] Bug- and style-fixes - looks like I committed incorrect files. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/rewind@13471 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/network/rewind_manager.cpp | 9 +++++---- src/network/rewind_manager.hpp | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index 166c1e991..69b49e2ba 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -295,9 +295,9 @@ void RewindManager::rewindTo(float rewind_time) } // while rewind info at time current_time // Store the time to which we have to replay to - float current_time = World::getWorld()->getTime(); + current_time = World::getWorld()->getTime(); - World::setTime(current_time); + World::getWorld()->setTime(current_time); // Now go forward through the saved states, and // replay taking the events into account. @@ -307,10 +307,11 @@ void RewindManager::rewindTo(float rewind_time) // Find the next event which needs to be taken into account // All other states can be deleted // TODO ... for now - while(state < m_rewind_info.size() && !m_rewind_info[state]->isEvent()) + while(state < (int)m_rewind_info.size() && + !m_rewind_info[state]->isEvent()) state ++; float dt = determineTimeStepSize(state); - World::getWorld()->update(dt); + World::getWorld()->updateWorld(dt); } diff --git a/src/network/rewind_manager.hpp b/src/network/rewind_manager.hpp index 72d5647e1..2de91f42b 100644 --- a/src/network/rewind_manager.hpp +++ b/src/network/rewind_manager.hpp @@ -117,7 +117,7 @@ private: m_rewinder->rewindToEvent(m_buffer); else { - if(m_is_confirmed) + if(m_is_confirmed) m_rewinder->rewindToState(m_buffer); else { @@ -147,7 +147,7 @@ private: ~RewindManager(); unsigned int findFirstIndex(float time) const; void insertRewindInfo(RewindInfo *ri); - + float determineTimeStepSize(int state); public: // First static functions to manage rewinding. // =========================================== From 999d2dd82c101abbc02d45b28545eb395ad393af Mon Sep 17 00:00:00 2001 From: hikerstk Date: Thu, 15 Aug 2013 06:19:35 +0000 Subject: [PATCH 004/350] Add the control settings to the state, that solves the problem of determining the control states when rewinding. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/rewind@13482 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/karts/kart_rewinder.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/karts/kart_rewinder.cpp b/src/karts/kart_rewinder.cpp index 08fc380b2..e399ffc92 100644 --- a/src/karts/kart_rewinder.cpp +++ b/src/karts/kart_rewinder.cpp @@ -39,7 +39,7 @@ KartRewinder::KartRewinder(AbstractKart *kart) : Rewinder(/*can_be_destroyed*/ f */ int KartRewinder::getState(char **buffer) const { - const int MEMSIZE = 13*sizeof(float); + const int MEMSIZE = 13*sizeof(float) + 9; *buffer = new char[MEMSIZE]; float* p = (float*)*buffer; @@ -57,6 +57,7 @@ int KartRewinder::getState(char **buffer) const memcpy(p+ 3, &q, 4*sizeof(float)); memcpy(p+ 7, body->getLinearVelocity(), 3*sizeof(float)); memcpy(p+10, body->getAngularVelocity(), 3*sizeof(float)); + m_kart->getControls().copyToMemory((char*)(p+13)); return MEMSIZE; } // getState @@ -90,6 +91,8 @@ void KartRewinder::rewindToState(char *buffer) body->setLinearVelocity(*(btVector3*)(p+7)); body->setAngularVelocity(*(btVector3*)(p+10)); + m_kart->getControls().setFromMemory((char*)(p+13)); + return; } // rewindToState From 8ed8963c638b19ff814dc7bd0d0a68c01e38d5b5 Mon Sep 17 00:00:00 2001 From: hikerstk Date: Thu, 15 Aug 2013 06:20:31 +0000 Subject: [PATCH 005/350] Documented the main loop of STK. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/rewind@13483 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/main_loop.cpp | 66 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/main_loop.cpp b/src/main_loop.cpp index 8524dbed0..754c6eaaf 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -114,6 +114,72 @@ void MainLoop::updateRace(float dt) //----------------------------------------------------------------------------- /** Run the actual main loop. + * The sequnce in which various parts of STK are updated is: + * - Determine next time step size (`getLimitedDt`). This takes maximum fps + * into account (i.e. sleep if the fps would be too high), and will actually + * slow down the in-game clock if the fps are too low (if more than 3/60 of + * a second have passed, more than 3 physics time steps would be needed, + * and physics do at most 3 time steps). + * - if a race is taking place (i.e. not only a menu being shown), call + * `updateRace()`, which is a thin wrapper around a call to + * `World::updateWorld()`: + * - Update history manager (which will either set the kart position and/or + * controls when replaying, or store the current info for a replay). + * This is mostly for debugging only (though available even in release + * mode). + * - Updates Replays - either storing data when not replaying, or + * updating kart positions/control when replaying). + * - Calls `WorldStatus::update()`, which updates the race state (e.g. + * go from 'ready' to 'set' etc), and clock. + * - Updates the physics (`Physics::update()`). This will simulate all + * physical objects for the specified time with bullet. + * - Updates all karts (`Kart::update()`). Obviously the update function + * does a lot more than what is described here, this is only supposed to + * be a _very_ high level overview: + * - Updates its rewinder (to store potentially changed controls + * as events) in `KartRewinder::update()`. + * - Calls `Moveable::update()`, which takes the new position from + * the physics and saves it (and computes dependent values, like + * heading, local velocity). + * - Updates its controller. This is either: + * - an AI using `SkiddingController::update()` (which then will + * compute the new controls), or + * - a player controller using `PlayerController::update()`, which will + * handle smooth steering (in case of digital input devices steering + * is adjusted a bit over time to avoid an instant change from all + * left to all right). Input events will be handled when updating + * the irrlicht driver later at the end of the main loop. + * - Updates kart animation (like rescue, ...) if one is shown atm. + * - Update attachments. + * - update physics, i.e. taking the current steering and updating + * the bullet raycast vehicle with that data. The settings are actually + * only used in the next frame when the physics are updated. + * - Updates all cameras via `Camera::update()`. The camera position and + * rotation is adjusted according to the position etc of the kart (and + * special circumstances like rescue, falling). + * - Updates all projectiles using the projectile manager. Some of the + * projectiles are mostly handled by the physics (e.g. a cake will mainly + * check if it's out of bounds), others (like basket ball) do all + * their aiming and movement here. + * - Updates the rewind manager to store rewind states. + * - Updates the music manager. + * - Updates the input manager (which only updates internal time, actual + * input handling follows late) + * - Updates the wiimote manager. This will read the data of all wiimotes + * and feed the corresponding events to the irrlicht event system. + * - Updates the STK internal gui engine. This updates all widgets, and + * e.g. takes care of the rotation of the karts in the KartSelection + * screen using the ModelViewWidget. + * - Updates STK's irrlicht driver `IrrDriver::update()`: + * - Calls Irrlicht's `beginScene()` . + * - Renders the scene (several times with different viewport if + * split screen is being used) + * - Calls `GUIEngine::render()`, which renders all widgets with the + * help of Irrlicht's GUIEnvironment (`drawAll()`). This will also + * handle all events, i.e. all input is now handled (e.g. steering, + * firing etc are all set in the corresponding karts depending on + * user input). + * - Calls Irrlicht's `endScene()` */ void MainLoop::run() { From f78cb2d1f2744e3dd64e16ecf8f57bdd9ad740cd Mon Sep 17 00:00:00 2001 From: hikerstk Date: Thu, 15 Aug 2013 06:23:08 +0000 Subject: [PATCH 006/350] Rewinding now can replay states and events, though there are still (initially) minor differences (caused most likely by the controls not set at the right time (i.e. one frame too early or too late). git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/rewind@13484 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/network/rewind_manager.cpp | 77 ++++++++++++++++++++++++++-------- src/network/rewind_manager.hpp | 61 +++++++++++++++++++++++++-- 2 files changed, 117 insertions(+), 21 deletions(-) diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index 69b49e2ba..54c29f888 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -89,7 +89,11 @@ void RewindManager::reset() m_count_of_comparisons = 0; m_count_of_searches = 0; #endif - m_overall_state_size = 0; + m_is_rewinding = false; + m_overall_state_size = 0; + m_state_frequency = 0.1f; // save 10 states a second + m_last_saved_state = -9999.9f; // forces initial state save + if(!m_enable_rewind_manager) return; AllRewinder::iterator r = m_all_rewinder.begin(); @@ -203,6 +207,7 @@ unsigned int RewindManager::findFirstIndex(float target_time) const */ void RewindManager::addEvent(Rewinder *rewinder, float time, char *buffer) { + if(m_is_rewinding) return; RewindInfo *ri = new RewindInfo(rewinder, time, buffer, /*is_event*/true, /*is_confirmed*/true); insertRewindInfo(ri); @@ -215,9 +220,13 @@ void RewindManager::addEvent(Rewinder *rewinder, float time, char *buffer) */ void RewindManager::update(float dt) { - if(!m_enable_rewind_manager || m_all_rewinder.size()==0) return; + if(!m_enable_rewind_manager || + m_all_rewinder.size()==0 || + m_is_rewinding ) return; float time = World::getWorld()->getTime(); + if(time - m_last_saved_state < m_state_frequency) + return; // For now always create a snapshot. for(unsigned int i=0; igetTime(); + float exact_rewind_time = m_rewind_info[state]->getTime(); // Rewind all objects (and also all state) that happen at the // current time. // TODO: this assumes atm that all rewinder have a initial state // for 'current_time'!!! - while(m_rewind_info[state]->getTime()==current_time) + while(m_rewind_info[state]->getTime()==exact_rewind_time) { m_rewind_info[state]->rewind(); state ++; } // while rewind info at time current_time // Store the time to which we have to replay to - current_time = World::getWorld()->getTime(); + float current_time = World::getWorld()->getTime(); - World::getWorld()->setTime(current_time); + World::getWorld()->setTime(exact_rewind_time); // Now go forward through the saved states, and // replay taking the events into account. - while(World::getWorld()->getTime() < current_time) + while(World::getWorld()->getTime() < current_time && + state < m_rewind_info.size()) { - // Find the next event which needs to be taken into account + // Find the next RewindInfo which needs to be taken into account // All other states can be deleted // TODO ... for now - while(state < (int)m_rewind_info.size() && - !m_rewind_info[state]->isEvent()) - state ++; - float dt = determineTimeStepSize(state); + int next_important_state = state; + while(next_important_state < (int)m_rewind_info.size() && + !m_rewind_info[next_important_state]->isEvent() && + !m_rewind_info[next_important_state]->isConfirmed() ) + { + // TODO discard/replace state + next_important_state ++; + } + + float dt = determineTimeStepSize(state, current_time); + float next_time = World::getWorld()->getTime() + dt; + // Now set all events and confirmed states + while(state < m_rewind_info.size() && + m_rewind_info[state]->getTime() <= next_time) + { + if(m_rewind_info[state]->isEvent() || + m_rewind_info[state]->isConfirmed() ) + { + m_rewind_info[state]->rewind(); + } + state++; + } World::getWorld()->updateWorld(dt); +#define SHOW_ROLLBACK +#ifdef SHOW_ROLLBACK + irr_driver->update(dt); +#endif } + m_is_rewinding = false; } // rewindTo @@ -321,14 +358,20 @@ void RewindManager::rewindTo(float rewind_time) /** Determines the next time step size to use when recomputing the physics. * The time step size is either 1/60 (default physics), or less, if there * is an even to handle before that time. - * \param state The next state to replay. + * \param next_state The next state to replay. + * \param end_time The end time to which we must replay forward. Don't + * return a dt that would be bigger tham this value. * \return The time step size to use in the next simulation step. */ -float RewindManager::determineTimeStepSize(int state) +float RewindManager::determineTimeStepSize(int next_state, float end_time) { float dt = 1.0f/60.0f; float t = World::getWorld()->getTime(); - if(m_rewind_info[state]->getTime() < t + dt) - return t-m_rewind_info[state]->getTime(); - return dt; + if(m_rewind_info[next_state]->getTime() < t + dt) + { + // Since we have RewindInfo at that time, it is certain that + /// this time is before (or at) end_time, not after. + return m_rewind_info[next_state]->getTime()-t; + } + return t+dt < end_time ? dt : end_time - t; } // determineTimeStepSize diff --git a/src/network/rewind_manager.hpp b/src/network/rewind_manager.hpp index 2de91f42b..53150cbd0 100644 --- a/src/network/rewind_manager.hpp +++ b/src/network/rewind_manager.hpp @@ -25,6 +25,50 @@ #include #include +/** \ingroup network + * This class manages rewinding. It keeps track of: + * - states for each rewindable object (for example a kart would have + * its position, rotation, linear and angular velocity etc as state) + * States can be confirmed (i.e. were received by the network server + * and are therefore confirmed to be conrrect), or not (just a snapshot + * on this client, which can save time in rewinding later). + * - events for each rewindable object (for example any change in the kart + * controls, like steering, fire, ... are an event). While states can be + * discarded (especially unconfirmed ones), e.g. to save spave, events + * will always be kept (in order to allow replaying). + * Each object that is to be rewinded an instance of Rewinder needs to be + * declared (usually inside of the object it can rewind). This instance + * is automatically registered with the RewindManager. + * All states and events are stored in a RewindInfo object. All RewindInfo + * objects are stored in a list sorted by time. + * When a rewind to time T is requested, the following takes place: + * 1. Go back in time: + * Determine the latest time t_min < T so that each rewindable objects + * has at least one state before T. For each state that is skipped during + * this process `undoState()` is being called, and for each event + * `undoEvent()` of the Rewinder. + * 2. Restore state at time `t_min` + * For each Rewinder the state at time t_min is restored by calling + * `rewindToState(char *)`. + * TODO: atm there is no guarantee that each object will have a state + * at a given time. We either need to work around that, or make sure + * to store at least an unconfirmed state whenever we receive a + * confirmed state. + * 3. Rerun the simulation till the current time t_current is reached: + * 1. Determine the time `t_next` of the next frame. This is either + * current_time + 1/60 (physics default time step size), or less + * if RewindInfo at an earlier time is available). + * This determines the time step size for the next frame (i.e. + * `t_next - t_current`). + * 2. For all RewindInfo at time t_next call: + * - `restoreState()` if the RewindInfo is a confirmed state + * - `discardState()` if the RewindInfo is an unconfirmed state + * TODO: still missing, and instead of discard perhaps + * store a new state?? + * - `rewindToEvent()` if the RewindInfo is an event + * 3. Do one step of world simulation, using the updated (confirmed) + * states and newly set events (e.g. kart input). + */ class RewindManager { @@ -41,9 +85,6 @@ private: /** A list of all objects that can be rewound. */ AllRewinder m_all_rewinder; - /** Overall amount of memory allocated by states. */ - unsigned int m_overall_state_size; - // ======================================================================== /** Used to store rewind information for a given time for all rewind * instances. @@ -134,6 +175,18 @@ private: AllRewindInfo m_rewind_info; + /** Overall amount of memory allocated by states. */ + unsigned int m_overall_state_size; + + /** Indicates if currently a rewind is happening. */ + bool m_is_rewinding; + + /** How much time between consecutive state saves. */ + float m_state_frequency; + + /** Time at which the last state was saved. */ + float m_last_saved_state; + #define REWIND_SEARCH_STATS #ifdef REWIND_SEARCH_STATS @@ -147,7 +200,7 @@ private: ~RewindManager(); unsigned int findFirstIndex(float time) const; void insertRewindInfo(RewindInfo *ri); - float determineTimeStepSize(int state); + float determineTimeStepSize(int state, float max_time); public: // First static functions to manage rewinding. // =========================================== From 0730359c3d30c9a53175479674372e333db6d172 Mon Sep 17 00:00:00 2001 From: hikerstk Date: Thu, 15 Aug 2013 22:54:57 +0000 Subject: [PATCH 007/350] Fixed bug in restoring kart controls. Added restoring controls to kart controller. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/rewind@13486 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/karts/controller/kart_control.hpp | 6 ++-- src/karts/kart_rewinder.cpp | 40 +++++++++++++++------------ src/karts/kart_rewinder.hpp | 4 +-- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/karts/controller/kart_control.hpp b/src/karts/controller/kart_control.hpp index f5a5ba680..27f89b438 100644 --- a/src/karts/controller/kart_control.hpp +++ b/src/karts/controller/kart_control.hpp @@ -114,9 +114,9 @@ public: /** Restores this object from a previously saved memory buffer. */ void setFromMemory(char *buffer) { - memcpy(&m_steer, buffer, sizeof(float)); - memcpy(&m_accel, buffer+4*sizeof(float), sizeof(float)); - setButtonsCompressed(buffer[2*sizeof(float)]); + memcpy(&m_steer, buffer , sizeof(float)); + memcpy(&m_accel, buffer+ sizeof(float), sizeof(float)); + setButtonsCompressed(buffer[2*sizeof(float)] ); } // setFromMemory // ------------------------------------------------------------------------ diff --git a/src/karts/kart_rewinder.cpp b/src/karts/kart_rewinder.cpp index e399ffc92..c832f78e6 100644 --- a/src/karts/kart_rewinder.cpp +++ b/src/karts/kart_rewinder.cpp @@ -61,23 +61,6 @@ int KartRewinder::getState(char **buffer) const return MEMSIZE; } // getState -// ---------------------------------------------------------------------------- -/** Called once a frame. It will add a new kart control event to the rewind - * manager if any control values have changed. - */ -void KartRewinder::update() -{ - if(m_kart->getControls() == m_previous_control) - return; - m_previous_control = m_kart->getControls(); - - char *buffer = new char[m_previous_control.getLength()]; - m_previous_control.copyToMemory(buffer); - - // The rewind manager will free the memory once it's not needed anymore - RewindManager::get()->addEvent(this, World::getWorld()->getTime(), buffer); -} // update - // ---------------------------------------------------------------------------- /** Actuall rewind to the specified state. */ void KartRewinder::rewindToState(char *buffer) @@ -96,4 +79,27 @@ void KartRewinder::rewindToState(char *buffer) return; } // rewindToState +// ---------------------------------------------------------------------------- +/** Called once a frame. It will add a new kart control event to the rewind + * manager if any control values have changed. + */ +void KartRewinder::update() +{ + if(m_kart->getControls() == m_previous_control) + return; + m_previous_control = m_kart->getControls(); + + char *buffer = new char[m_previous_control.getLength()]; + m_previous_control.copyToMemory(buffer); + + // The rewind manager will free the memory once it's not needed anymore + RewindManager::get()->addEvent(this, World::getWorld()->getTime(), buffer); +} // update + +// ---------------------------------------------------------------------------- +void KartRewinder::rewindToEvent(char *p) +{ + m_kart->getControls().setFromMemory(p); +}; // rewindToEvent + diff --git a/src/karts/kart_rewinder.hpp b/src/karts/kart_rewinder.hpp index 806384261..28c9b3b30 100644 --- a/src/karts/kart_rewinder.hpp +++ b/src/karts/kart_rewinder.hpp @@ -39,9 +39,7 @@ public: virtual ~KartRewinder() {}; virtual int getState(char **buffer) const; virtual void rewindToState(char *p) OVERRIDE; - virtual void rewindToEvent(char *p) OVERRIDE - { - }; // rewindToEvent + virtual void rewindToEvent(char *p) OVERRIDE; // ------------------------------------------------------------------------- virtual void undoState(char *p) OVERRIDE From c4d120e1b372392154f4b83a53443988f9cb92c1 Mon Sep 17 00:00:00 2001 From: hikerstk Date: Wed, 21 Aug 2013 21:42:18 +0000 Subject: [PATCH 008/350] Stored the time stamp of the main simulation, so that during a rewind the same time steps can be taken (that's mostly meant for debugging). git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/rewind@13534 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/network/rewind_manager.cpp | 37 +++++++++++++++++++++++++--------- src/network/rewind_manager.hpp | 26 ++++++++++++++++-------- 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index 54c29f888..a986e6b8e 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -54,10 +54,20 @@ RewindManager::RewindInfo::RewindInfo(Rewinder *rewinder, float time, m_rewinder = rewinder; m_time = time; m_buffer = buffer; - m_is_event = is_event; + m_type = is_event ? RIT_EVENT : RIT_STATE; m_is_confirmed = is_confirmed; } // RewindInfo +// ---------------------------------------------------------------------------- +RewindManager::RewindInfo::RewindInfo(float time) +{ + m_rewinder = NULL; + m_time = time; + m_buffer = NULL; + m_type = RIT_TIME; + m_is_confirmed = true; +} // RewindInfo + // ============================================================================ /** The constructor. */ @@ -187,7 +197,8 @@ unsigned int RewindManager::findFirstIndex(float target_time) const #ifdef REWIND_SEARCH_STATS m_count_of_comparisons++; #endif - if(m_rewind_info[index]->getTime()getTime()isTime() ) return index; index--; } @@ -222,11 +233,15 @@ void RewindManager::update(float dt) { if(!m_enable_rewind_manager || m_all_rewinder.size()==0 || - m_is_rewinding ) return; - + m_is_rewinding ) return; + float time = World::getWorld()->getTime(); if(time - m_last_saved_state < m_state_frequency) + { + RewindInfo *ri = new RewindInfo(World::getWorld()->getTime()); + insertRewindInfo(ri); return; + } // For now always create a snapshot. for(unsigned int i=0; iisEvent()) + if(!m_rewind_info[state]->isState()) { Log::error("RewindManager", "No state for rewind to %d, state %d.", rewind_time, state); @@ -315,7 +330,7 @@ void RewindManager::rewindTo(float rewind_time) // Now go forward through the saved states, and // replay taking the events into account. while(World::getWorld()->getTime() < current_time && - state < m_rewind_info.size()) + state < (int)m_rewind_info.size()) { // Find the next RewindInfo which needs to be taken into account @@ -323,8 +338,8 @@ void RewindManager::rewindTo(float rewind_time) // TODO ... for now int next_important_state = state; while(next_important_state < (int)m_rewind_info.size() && - !m_rewind_info[next_important_state]->isEvent() && - !m_rewind_info[next_important_state]->isConfirmed() ) + m_rewind_info[next_important_state]->isState() && + !m_rewind_info[next_important_state]->isConfirmed() ) { // TODO discard/replace state next_important_state ++; @@ -333,11 +348,11 @@ void RewindManager::rewindTo(float rewind_time) float dt = determineTimeStepSize(state, current_time); float next_time = World::getWorld()->getTime() + dt; // Now set all events and confirmed states - while(state < m_rewind_info.size() && + while(state < (int)m_rewind_info.size() && m_rewind_info[state]->getTime() <= next_time) { if(m_rewind_info[state]->isEvent() || - m_rewind_info[state]->isConfirmed() ) + m_rewind_info[state]->isConfirmed() ) { m_rewind_info[state]->rewind(); } @@ -365,6 +380,8 @@ void RewindManager::rewindTo(float rewind_time) */ float RewindManager::determineTimeStepSize(int next_state, float end_time) { + return m_rewind_info[next_state]->getTime()-World::getWorld()->getTime(); + float dt = 1.0f/60.0f; float t = World::getWorld()->getTime(); if(m_rewind_info[next_state]->getTime() < t + dt) diff --git a/src/network/rewind_manager.hpp b/src/network/rewind_manager.hpp index 53150cbd0..3f4130894 100644 --- a/src/network/rewind_manager.hpp +++ b/src/network/rewind_manager.hpp @@ -99,14 +99,17 @@ private: class RewindInfo { private: + /** The different information types that can be saved. */ + enum RewindInfoType {RIT_TIME, RIT_STATE, RIT_EVENT}; + /** Pointer to the buffer which stores all states. */ char *m_buffer; /** Time when this state was taken. */ float m_time; - /** True if this is an event, and not a state. */ - bool m_is_event; + /** Type of this information. */ + RewindInfoType m_type; /** A confirmed event is one that was sent from the server. When * rewinding we have to start with a confirmed state for each @@ -119,6 +122,8 @@ private: RewindInfo(Rewinder *rewinder, float time, char *buffer, bool is_event, bool is_confirmed); // -------------------------------------------------------------------- + RewindInfo(float time); + // -------------------------------------------------------------------- ~RewindInfo() { delete m_buffer; @@ -130,7 +135,11 @@ private: /** Returns the time at which this rewind state was saved. */ float getTime() const { return m_time; } // -------------------------------------------------------------------- - bool isEvent() const { return m_is_event; } + bool isEvent() const { return m_type==RIT_EVENT; } + // -------------------------------------------------------------------- + bool isTime() const { return m_type==RIT_TIME; } + // -------------------------------------------------------------------- + bool isState() const { return m_type==RIT_STATE; } // -------------------------------------------------------------------- /** Returns if this state is confirmed. */ bool isConfirmed() const { return m_is_confirmed; } @@ -139,10 +148,11 @@ private: * It calls either undoEvent or undoState in the rewinder. */ void undo() { - if(m_is_event) + if(m_type==RIT_EVENT) m_rewinder->undoEvent(m_buffer); - else + else if(m_type==RIT_STATE) m_rewinder->undoState(m_buffer); + // time evnet can be ignored. } // undoEvent // -------------------------------------------------------------------- /** Rewinds to this state. This is called while going forwards in time @@ -154,9 +164,9 @@ private: */ void rewind() { - if(m_is_event) + if(m_type==RIT_EVENT) m_rewinder->rewindToEvent(m_buffer); - else + else if(m_type==RIT_STATE) { if(m_is_confirmed) m_rewinder->rewindToState(m_buffer); @@ -165,7 +175,7 @@ private: // TODO // Handle replacing of stored states. } - } + } // time information can be ignored } // rewind }; // RewindInfo // ======================================================================== From cb46fad1243ab47e199c8c141019a2554c5c9b51 Mon Sep 17 00:00:00 2001 From: hikerstk Date: Wed, 4 Sep 2013 22:06:26 +0000 Subject: [PATCH 009/350] 1) Fixed time when state snapshots are taken (must be before the physics runs) 2) Added support for storing the exact time step sizes (probably only for debugging - since now we can try to have bitwise identical results when replaying). git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/rewind@13631 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/karts/kart.cpp | 20 +++++++++++++------- src/modes/world.cpp | 5 +++-- src/network/rewind_manager.cpp | 12 ++++++++++-- src/network/rewind_manager.hpp | 8 +++++++- src/physics/stk_dynamics_world.hpp | 9 +++++++-- 5 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 60ac56178..6ae969ddb 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1060,8 +1060,6 @@ void Kart::eliminate() */ void Kart::update(float dt) { - m_rewinder->update(); - if ( UserConfigParams::m_graphical_effects ) { // update star effect (call will do nothing if stars are not activated) @@ -1336,6 +1334,10 @@ void Kart::update(float dt) m_shadow->enableShadow(); m_shadow_enabled = true; } + + // Store current controls if there was any change from the previous state: + m_rewinder->update(); + } // update //----------------------------------------------------------------------------- @@ -1944,11 +1946,15 @@ void Kart::updatePhysics(float dt) { m_has_started = true; float f = m_kart_properties->getStartupBoost(); - m_max_speed->instantSpeedIncrease(MaxSpeed::MS_INCREASE_ZIPPER, - 0.9f*f, f, - /*engine_force*/200.0f, - /*duration*/5.0f, - /*fade_out_time*/5.0f); + + // Only give the additional engine_force if a boost was reached + // (otherwise even with f==0, the 200 engine force would apply) + if(f>0) + m_max_speed->instantSpeedIncrease(MaxSpeed::MS_INCREASE_ZIPPER, + 0.9f*f, f, + /*engine_force*/200.0f, + /*duration*/5.0f, + /*fade_out_time*/5.0f); } m_bounce_back_time-=dt; diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 5d40e5239..38359763f 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -831,6 +831,9 @@ void World::update(float dt) // Clear race state so that new information can be stored race_state->clear(); + RewindManager::get()->saveStates(dt); + + if(network_manager->getMode()!=NetworkManager::NW_CLIENT && !history->dontDoPhysics()) { @@ -851,8 +854,6 @@ void World::update(float dt) projectile_manager->update(dt); - RewindManager::get()->update(dt); - PROFILER_POP_CPU_MARKER(); #ifdef DEBUG diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index a986e6b8e..f81e2b69c 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -20,6 +20,8 @@ #include "modes/world.hpp" #include "network/rewinder.hpp" +#include "physics/physics.hpp" +#include "race/history.hpp" #include "utils/log.hpp" RewindManager* RewindManager::m_rewind_manager = NULL; @@ -53,6 +55,7 @@ RewindManager::RewindInfo::RewindInfo(Rewinder *rewinder, float time, { m_rewinder = rewinder; m_time = time; + m_local_physics_time = World::getWorld()->getPhysics()->getPhysicsWorld()->getLocalTime(); m_buffer = buffer; m_type = is_event ? RIT_EVENT : RIT_STATE; m_is_confirmed = is_confirmed; @@ -63,6 +66,7 @@ RewindManager::RewindInfo::RewindInfo(float time) { m_rewinder = NULL; m_time = time; + m_local_physics_time = World::getWorld()->getPhysics()->getPhysicsWorld()->getLocalTime(); m_buffer = NULL; m_type = RIT_TIME; m_is_confirmed = true; @@ -229,7 +233,7 @@ void RewindManager::addEvent(Rewinder *rewinder, float time, char *buffer) * rewinder to do so. * \param dt Time step size. */ -void RewindManager::update(float dt) +void RewindManager::saveStates(float dt) { if(!m_enable_rewind_manager || m_all_rewinder.size()==0 || @@ -265,7 +269,8 @@ void RewindManager::update(float dt) float(m_count_of_comparisons)/ float(m_count_of_searches) ); m_last_saved_state = time; -} // update +} // saveStates + // ---------------------------------------------------------------------------- /** Rewinds to the specified time. * \param t Time to rewind to. @@ -275,6 +280,8 @@ void RewindManager::rewindTo(float rewind_time) assert(!m_is_rewinding); m_is_rewinding = true; + history->doReplayHistory(History::HISTORY_NONE); + // First find the state to which we need to rewind // ------------------------------------------------ int state = findFirstIndex(rewind_time); @@ -311,6 +318,7 @@ void RewindManager::rewindTo(float rewind_time) // Rewind to the required state // ---------------------------- float exact_rewind_time = m_rewind_info[state]->getTime(); + World::getWorld()->getPhysics()->getPhysicsWorld()->setLocalTime(m_rewind_info[state]->getLocalPhysicsTime()); // Rewind all objects (and also all state) that happen at the // current time. diff --git a/src/network/rewind_manager.hpp b/src/network/rewind_manager.hpp index 3f4130894..99d146704 100644 --- a/src/network/rewind_manager.hpp +++ b/src/network/rewind_manager.hpp @@ -108,6 +108,9 @@ private: /** Time when this state was taken. */ float m_time; + /** The 'left over' time from the physics. */ + float m_local_physics_time; + /** Type of this information. */ RewindInfoType m_type; @@ -144,6 +147,9 @@ private: /** Returns if this state is confirmed. */ bool isConfirmed() const { return m_is_confirmed; } // -------------------------------------------------------------------- + /** Returns the left-over physics time. */ + float getLocalPhysicsTime() const { return m_local_physics_time; } + // -------------------------------------------------------------------- /** Called when going back in time to undo any rewind information. * It calls either undoEvent or undoState in the rewinder. */ void undo() @@ -236,7 +242,7 @@ public: // ------------------------------------------------------------------------ void reset(); - void update(float dt); + void saveStates(float dt); // ------------------------------------------------------------------------ /** Adds a Rewinder to the list of all rewinders. * \return true If rewinding is enabled, false otherwise. diff --git a/src/physics/stk_dynamics_world.hpp b/src/physics/stk_dynamics_world.hpp index 5be3db306..4187e5d18 100644 --- a/src/physics/stk_dynamics_world.hpp +++ b/src/physics/stk_dynamics_world.hpp @@ -38,8 +38,13 @@ public: /** Resets m_localTime to 0. This allows more precise replay of * physics, which is important for replaying histories. */ - virtual void resetLocalTime() { m_localTime = 0; } - + void resetLocalTime() { m_localTime = 0; } + // ------------------------------------------------------------------------ + /** Sets the local time to a specified value. Used in rewinding. */ + void setLocalTime(float t) { m_localTime = t; } + // ------------------------------------------------------------------------ + /** Gets the local time. */ + float getLocalTime() const { return m_localTime; } }; // STKDynamicsWorld #endif /* EOF */ From 891df54f3a9d13221ab5458baee958d51540f1c3 Mon Sep 17 00:00:00 2001 From: hikerstk Date: Mon, 23 Sep 2013 06:57:32 +0000 Subject: [PATCH 010/350] Fixed rewind (events had a time stamp after increasing world clock, while states had a time stamp before world clock was updated). Still all work in progress ;) git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/rewind@14138 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/karts/kart.cpp | 11 ++++---- src/karts/kart_rewinder.cpp | 2 +- src/modes/world.cpp | 5 ++-- src/network/rewind_manager.cpp | 50 ++++++++++++---------------------- src/network/rewind_manager.hpp | 46 +++++++++++++++++++++++++++---- 5 files changed, 68 insertions(+), 46 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 6ae969ddb..63908b96f 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -52,8 +52,9 @@ #include "karts/max_speed.hpp" #include "karts/skidding.hpp" #include "modes/linear_world.hpp" -#include "network/race_state.hpp" #include "network/network_manager.hpp" +#include "network/rewind_manager.hpp" +#include "network/race_state.hpp" #include "physics/btKart.hpp" #include "physics/btKartRaycast.hpp" #include "physics/btUprightConstraint.hpp" @@ -530,7 +531,7 @@ void Kart::blockViewWithPlunger() if(isShielded()) { decreaseShieldTime(0.0f); //decrease the default amount of time - Log::verbose("Kart", "Decreasing shield, because of removing the plunger. \n"); + Log::verbose("Kart", "Decreasing shield, because of removing the plunger."); } } // blockViewWithPlunger @@ -1088,7 +1089,7 @@ void Kart::update(float dt) // Update the position and other data taken from the physics Moveable::update(dt); - if(!history->replayHistory()) + if(!history->replayHistory() && !RewindManager::get()->isRewinding()) m_controller->update(dt); // if its view is blocked by plunger, decrease remaining time @@ -1362,7 +1363,7 @@ void Kart::setSquash(float time, float slowdown) if (isShielded()) { decreaseShieldTime(stk_config->m_bubblegum_shield_time/2.0f); - Log::verbose("Kart", "Decreasing shield \n"); + Log::verbose("Kart", "Decreasing shield"); return; } @@ -2043,7 +2044,7 @@ void Kart::updatePhysics(float dt) ); #endif -} // updatePhysics +} // updatephysics //----------------------------------------------------------------------------- /** Adjust the engine sound effect depending on the speed of the kart. diff --git a/src/karts/kart_rewinder.cpp b/src/karts/kart_rewinder.cpp index c832f78e6..c247676f9 100644 --- a/src/karts/kart_rewinder.cpp +++ b/src/karts/kart_rewinder.cpp @@ -93,7 +93,7 @@ void KartRewinder::update() m_previous_control.copyToMemory(buffer); // The rewind manager will free the memory once it's not needed anymore - RewindManager::get()->addEvent(this, World::getWorld()->getTime(), buffer); + RewindManager::get()->addEvent(this, buffer); } // update // ---------------------------------------------------------------------------- diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 38359763f..e4ca37654 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -827,11 +827,12 @@ void World::update(float dt) if(ReplayRecorder::get()) ReplayRecorder::get()->update(dt); if(ReplayPlay::get()) ReplayPlay::get()->update(dt); if(history->replayHistory()) dt=history->getNextDelta(); - WorldStatus::update(dt); // Clear race state so that new information can be stored race_state->clear(); - RewindManager::get()->saveStates(dt); + RewindManager::get()->setCurrentTime(World::getWorld()->getTime(), dt); + RewindManager::get()->saveStates(); + WorldStatus::update(dt); if(network_manager->getMode()!=NetworkManager::NW_CLIENT && diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index f81e2b69c..356e4f288 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -49,12 +49,12 @@ void RewindManager::destroy() * for all state info. * \param size Necessary buffer size for a state. */ -RewindManager::RewindInfo::RewindInfo(Rewinder *rewinder, float time, - char *buffer, bool is_event, - bool is_confirmed) +RewindManager::RewindInfo::RewindInfo(Rewinder *rewinder, char *buffer, + bool is_event, bool is_confirmed) { m_rewinder = rewinder; - m_time = time; + m_time = RewindManager::get()->getCurrentTime();; + m_time_step = RewindManager::get()->getCurrentTimeStep(); m_local_physics_time = World::getWorld()->getPhysics()->getPhysicsWorld()->getLocalTime(); m_buffer = buffer; m_type = is_event ? RIT_EVENT : RIT_STATE; @@ -62,10 +62,11 @@ RewindManager::RewindInfo::RewindInfo(Rewinder *rewinder, float time, } // RewindInfo // ---------------------------------------------------------------------------- -RewindManager::RewindInfo::RewindInfo(float time) +RewindManager::RewindInfo::RewindInfo() { m_rewinder = NULL; - m_time = time; + m_time = RewindManager::get()->getCurrentTime(); + m_time_step = RewindManager::get()->getCurrentTimeStep(); m_local_physics_time = World::getWorld()->getPhysics()->getPhysicsWorld()->getLocalTime(); m_buffer = NULL; m_type = RIT_TIME; @@ -220,10 +221,10 @@ unsigned int RewindManager::findFirstIndex(float target_time) const * \param time Time at which the event was recorded. * \param buffer Pointer to the event data. */ -void RewindManager::addEvent(Rewinder *rewinder, float time, char *buffer) +void RewindManager::addEvent(Rewinder *rewinder, char *buffer) { if(m_is_rewinding) return; - RewindInfo *ri = new RewindInfo(rewinder, time, buffer, /*is_event*/true, + RewindInfo *ri = new RewindInfo(rewinder, buffer, /*is_event*/true, /*is_confirmed*/true); insertRewindInfo(ri); } // addEvent @@ -233,7 +234,7 @@ void RewindManager::addEvent(Rewinder *rewinder, float time, char *buffer) * rewinder to do so. * \param dt Time step size. */ -void RewindManager::saveStates(float dt) +void RewindManager::saveStates() { if(!m_enable_rewind_manager || m_all_rewinder.size()==0 || @@ -242,7 +243,7 @@ void RewindManager::saveStates(float dt) float time = World::getWorld()->getTime(); if(time - m_last_saved_state < m_state_frequency) { - RewindInfo *ri = new RewindInfo(World::getWorld()->getTime()); + RewindInfo *ri = new RewindInfo(); insertRewindInfo(ri); return; } @@ -255,7 +256,7 @@ void RewindManager::saveStates(float dt) if(size>=0) { m_overall_state_size += size; - RewindInfo *ri = new RewindInfo(m_all_rewinder[i], time, p, + RewindInfo *ri = new RewindInfo(m_all_rewinder[i], p, /*is_event*/false, /*is_confirmed*/true); assert(ri); @@ -324,11 +325,6 @@ void RewindManager::rewindTo(float rewind_time) // current time. // TODO: this assumes atm that all rewinder have a initial state // for 'current_time'!!! - while(m_rewind_info[state]->getTime()==exact_rewind_time) - { - m_rewind_info[state]->rewind(); - state ++; - } // while rewind info at time current_time // Store the time to which we have to replay to float current_time = World::getWorld()->getTime(); @@ -337,27 +333,15 @@ void RewindManager::rewindTo(float rewind_time) // Now go forward through the saved states, and // replay taking the events into account. - while(World::getWorld()->getTime() < current_time && - state < (int)m_rewind_info.size()) + while( World::getWorld()->getTime() < current_time && + state < (int)m_rewind_info.size() ) { - // Find the next RewindInfo which needs to be taken into account - // All other states can be deleted - // TODO ... for now - int next_important_state = state; - while(next_important_state < (int)m_rewind_info.size() && - m_rewind_info[next_important_state]->isState() && - !m_rewind_info[next_important_state]->isConfirmed() ) - { - // TODO discard/replace state - next_important_state ++; - } - float dt = determineTimeStepSize(state, current_time); - float next_time = World::getWorld()->getTime() + dt; + // Now set all events and confirmed states while(state < (int)m_rewind_info.size() && - m_rewind_info[state]->getTime() <= next_time) + m_rewind_info[state]->getTime()<=World::getWorld()->getTime()+0.001f) { if(m_rewind_info[state]->isEvent() || m_rewind_info[state]->isConfirmed() ) @@ -388,6 +372,8 @@ void RewindManager::rewindTo(float rewind_time) */ float RewindManager::determineTimeStepSize(int next_state, float end_time) { + return m_rewind_info[next_state]->getTimeStep(); + return m_rewind_info[next_state]->getTime()-World::getWorld()->getTime(); float dt = 1.0f/60.0f; diff --git a/src/network/rewind_manager.hpp b/src/network/rewind_manager.hpp index 99d146704..7847949e6 100644 --- a/src/network/rewind_manager.hpp +++ b/src/network/rewind_manager.hpp @@ -108,6 +108,9 @@ private: /** Time when this state was taken. */ float m_time; + /** Time step size. */ + float m_time_step; + /** The 'left over' time from the physics. */ float m_local_physics_time; @@ -122,10 +125,10 @@ private: /** The Rewinder instance for which this data is. */ Rewinder *m_rewinder; public: - RewindInfo(Rewinder *rewinder, float time, char *buffer, + RewindInfo(Rewinder *rewinder, char *buffer, bool is_event, bool is_confirmed); // -------------------------------------------------------------------- - RewindInfo(float time); + RewindInfo(); // -------------------------------------------------------------------- ~RewindInfo() { @@ -138,6 +141,9 @@ private: /** Returns the time at which this rewind state was saved. */ float getTime() const { return m_time; } // -------------------------------------------------------------------- + /** Time step size. */ + float getTimeStep() const { return m_time_step; } + // -------------------------------------------------------------------- bool isEvent() const { return m_type==RIT_EVENT; } // -------------------------------------------------------------------- bool isTime() const { return m_type==RIT_TIME; } @@ -203,6 +209,15 @@ private: /** Time at which the last state was saved. */ float m_last_saved_state; + /** The current time to be used in all states/events. This is used to + * give all states and events during one frame the same time, even + * if e.g. states are saved before world time is increased, other + * events later. */ + float m_current_time; + + /** The current time step size. */ + float m_time_step; + #define REWIND_SEARCH_STATS #ifdef REWIND_SEARCH_STATS @@ -223,6 +238,24 @@ public: static RewindManager *create(); static void destroy(); // ------------------------------------------------------------------------ + /** Sets the time that is to be used for all further states or events, + * and the time step size. This is necessary so that states/events before + * and after World::m_time is increased have the same time stamp. + * \param t Time. + * \param dt Time step size. + */ + void setCurrentTime(float t, float dt) + { + m_current_time = t; + m_time_step = dt; + } // setCurrentTime + + // ------------------------------------------------------------------------ + /** Returns the current time. */ + float getCurrentTime() const { return m_current_time; } + // ------------------------------------------------------------------------ + float getCurrentTimeStep() const { return m_time_step; } + // ------------------------------------------------------------------------ /** En- or disables rewinding. */ static void setEnable(bool m) { m_enable_rewind_manager = m;} @@ -242,7 +275,9 @@ public: // ------------------------------------------------------------------------ void reset(); - void saveStates(float dt); + void saveStates(); + void rewindTo(float target_time); + void addEvent(Rewinder *rewinder, char *buffer); // ------------------------------------------------------------------------ /** Adds a Rewinder to the list of all rewinders. * \return true If rewinding is enabled, false otherwise. @@ -254,9 +289,8 @@ public: return true; } // addRewinder // ------------------------------------------------------------------------ - void rewindTo(float target_time); - // ------------------------------------------------------------------------ - void addEvent(Rewinder *rewinder, float time, char *buffer); + /** Returns true if currently a rewind is happening. */ + bool isRewinding() const { return m_is_rewinding; } }; // RewindManager From 527a8d674ba6a5862d54f0ba2d6cc4e8496683cb Mon Sep 17 00:00:00 2001 From: nixt Date: Fri, 6 Jun 2014 05:15:11 +0530 Subject: [PATCH 011/350] 3D kart localization implemented. --- src/karts/controller/skidding_ai.cpp | 3 +- src/karts/kart.cpp | 6 ++-- src/tracks/quad.cpp | 45 ++++++++++++++++++++++++++-- src/tracks/quad.hpp | 1 + src/utils/vec3.hpp | 9 ++++++ 5 files changed, 57 insertions(+), 7 deletions(-) diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index ee36e1745..b2ebd6a94 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -238,7 +238,8 @@ void SkiddingAI::update(float dt) // This is used to enable firing an item backwards. m_controls->m_look_back = false; m_controls->m_nitro = false; - + Log::info("Sector", "%d", m_track_node); + // Don't do anything if there is currently a kart animations shown. if(m_kart->getKartAnimation()) return; diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index cfc59616f..c0df7dc4f 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1109,9 +1109,9 @@ void Kart::update(float dt) // allow nice jumps without scripting the fly // Also disable he upright constraint when gravity is changed by // the terrain - if( (!isNearGround() && - m_max_speed->getSpeedIncreaseTimeLeft(MaxSpeed::MS_INCREASE_ZIPPER)<=0.0f ) || - (getMaterial() && getMaterial()->hasGravity()) ) + if ((!isNearGround() && + m_max_speed->getSpeedIncreaseTimeLeft(MaxSpeed::MS_INCREASE_ZIPPER) <= 0.0f) || + (getMaterial() && getMaterial()->hasGravity())) m_uprightConstraint->setLimit(M_PI); else m_uprightConstraint->setLimit(m_kart_properties->getUprightTolerance()); diff --git a/src/tracks/quad.cpp b/src/tracks/quad.cpp index 7f281e396..b95ee88d9 100644 --- a/src/tracks/quad.cpp +++ b/src/tracks/quad.cpp @@ -28,7 +28,10 @@ Quad::Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, bool invisible, bool ai_ignore) { - if(p1.sideOfLine2D(p0, p2)>0 || + + /*This check must be skipped because inverted quads may appear to have + incorrect oritentation. + if(p1.sideOfLine2D(p0, p2)>0 || p3.sideOfLine2D(p0, p2)<0) { printf("Warning: quad has wrong orientation: p0=%f %f %f p1=%f %f %f\n", @@ -38,9 +41,9 @@ Quad::Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, m_p[0]=p1; m_p[1]=p0; m_p[2]=p3; m_p[3]=p2; } else - { + {*/ m_p[0]=p0; m_p[1]=p1; m_p[2]=p2; m_p[3]=p3; - } + //} m_center = 0.25f*(p0+p1+p2+p3); m_min_height = std::min ( std::min(p0.getY(), p1.getY()), std::min(p2.getY(), p3.getY()) ); @@ -113,6 +116,42 @@ bool Quad::pointInQuad(const Vec3& p) const } } // pointInQuad +bool Quad::pointInQuad3D(const Vec3& p) const +{ + // All this code for computing the quad bounding box should be later be + // moved to somewhere in the constructor and computed only once per quad. + Vec3 boxCorners[8]; + Vec3 normal = (m_p[1] - m_p[0]).cross(m_p[2] - m_p[1]); + normal.normalize(); + float boxHigh = 5.0f; + float boxLow = 1.0f; + boxCorners[0] = m_p[0] + boxHigh*normal; + boxCorners[1] = m_p[1] + boxHigh*normal; + boxCorners[2] = m_p[2] + boxHigh*normal; + boxCorners[3] = m_p[3] + boxHigh*normal; + boxCorners[4] = m_p[0] + -boxLow*m_p[1]; + boxCorners[5] = m_p[1] + -boxLow*m_p[1]; + boxCorners[6] = m_p[2] + -boxLow*m_p[1]; + boxCorners[7] = m_p[3] + -boxLow*m_p[1]; + + const Vec3 boxFaces[6][4] = { + { boxCorners[0], boxCorners[1], boxCorners[2], boxCorners[3] }, + { boxCorners[3], boxCorners[2], boxCorners[6], boxCorners[7] }, + { boxCorners[7], boxCorners[6], boxCorners[5], boxCorners[4] }, + { boxCorners[1], boxCorners[0], boxCorners[4], boxCorners[5] }, + { boxCorners[4], boxCorners[0], boxCorners[3], boxCorners[7] }, + { boxCorners[1], boxCorners[5], boxCorners[6], boxCorners[2] } }; + + float side = p.sideofPlane(boxFaces[0][0], boxFaces[0][1], boxFaces[0][2]); + for (int i = 1; i < 6; i++) + { + if (side*p.sideofPlane(boxFaces[i][0], boxFaces[i][1], boxFaces[i][2]) < 0) return false; + } + return true; + +} + + // ---------------------------------------------------------------------------- /** Transforms a quad by a given transform (i.e. translation+rotation). This * function does not modify this quad, the results are stored in the quad diff --git a/src/tracks/quad.hpp b/src/tracks/quad.hpp index 4688ecb60..704cc1edd 100644 --- a/src/tracks/quad.hpp +++ b/src/tracks/quad.hpp @@ -62,6 +62,7 @@ public: bool invis=false, bool ai_ignore=false); void getVertices(video::S3DVertex *v, const video::SColor &color) const; bool pointInQuad(const Vec3& p) const; + bool pointInQuad3D(const Vec3& p) const; void transform(const btTransform &t, Quad *result) const; // ------------------------------------------------------------------------ /** Returns the i-th. point of a quad. */ diff --git a/src/utils/vec3.hpp b/src/utils/vec3.hpp index 77d894369..f554d0696 100644 --- a/src/utils/vec3.hpp +++ b/src/utils/vec3.hpp @@ -20,6 +20,7 @@ #ifndef HEADER_VEC3_HPP #define HEADER_VEC3_HPP +#include #include #include using namespace irr; @@ -204,6 +205,14 @@ public: (end.getZ()-start.getZ())*(m_floats[0]-start.getX()); } // sideOfLine2D + + float sideofPlane(const Vec3& x1, const Vec3& x2, const Vec3& x3) const + { + core::triangle3df triangle(x1.toIrrVector(), x2.toIrrVector(), x3.toIrrVector()); + core::vector3df normal = triangle.getNormal(); + return normal.dotProduct((*this - x1).toIrrVector()); + + } // sideOfPlane }; // Vec3 From b81a8fb0a78a8c0b90258ee2a542e99ee540a2c7 Mon Sep 17 00:00:00 2001 From: nixt Date: Fri, 6 Jun 2014 05:41:13 +0530 Subject: [PATCH 012/350] Forgot to change all instances of pointInQuad to pointInQuad3D --- src/tracks/quad_graph.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tracks/quad_graph.cpp b/src/tracks/quad_graph.cpp index 528519058..6d343b7eb 100644 --- a/src/tracks/quad_graph.cpp +++ b/src/tracks/quad_graph.cpp @@ -818,7 +818,7 @@ void QuadGraph::findRoadSector(const Vec3& xyz, int *sector, { // Most likely the kart will still be on the sector it was before, // so this simple case is tested first. - if(*sector!=UNKNOWN_SECTOR && getQuadOfNode(*sector).pointInQuad(xyz) ) + if(*sector!=UNKNOWN_SECTOR && getQuadOfNode(*sector).pointInQuad3D(xyz) ) { return; } // if still on same quad @@ -851,7 +851,7 @@ void QuadGraph::findRoadSector(const Vec3& xyz, int *sector, float dist = xyz.getY() - q.getMinHeight(); // While negative distances are unlikely, we allow some small negative // numbers in case that the kart is partly in the track. - if(q.pointInQuad(xyz) && dist < min_dist && dist>-1.0f) + if(q.pointInQuad3D(xyz) && dist < min_dist && dist>-1.0f) { min_dist = dist; *sector = indx; From 182122720eb85211e044eecda9a4ab6858899c5a Mon Sep 17 00:00:00 2001 From: nixt Date: Fri, 6 Jun 2014 06:43:41 +0530 Subject: [PATCH 013/350] Undo workaround for operator-(Vec3, btVector3) and remove Log message for debugging. --- src/karts/controller/skidding_ai.cpp | 2 +- src/tracks/quad.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index 310051cfe..3b8996cdf 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -238,7 +238,7 @@ void SkiddingAI::update(float dt) // This is used to enable firing an item backwards. m_controls->m_look_back = false; m_controls->m_nitro = false; - Log::info("Sector", "%d", m_track_node); + //Log::info("Sector", "%d", m_track_node); // Don't do anything if there is currently a kart animations shown. if(m_kart->getKartAnimation()) diff --git a/src/tracks/quad.cpp b/src/tracks/quad.cpp index b95ee88d9..8535ecc74 100644 --- a/src/tracks/quad.cpp +++ b/src/tracks/quad.cpp @@ -129,10 +129,10 @@ bool Quad::pointInQuad3D(const Vec3& p) const boxCorners[1] = m_p[1] + boxHigh*normal; boxCorners[2] = m_p[2] + boxHigh*normal; boxCorners[3] = m_p[3] + boxHigh*normal; - boxCorners[4] = m_p[0] + -boxLow*m_p[1]; - boxCorners[5] = m_p[1] + -boxLow*m_p[1]; - boxCorners[6] = m_p[2] + -boxLow*m_p[1]; - boxCorners[7] = m_p[3] + -boxLow*m_p[1]; + boxCorners[4] = m_p[0] - boxLow*m_p[1]; + boxCorners[5] = m_p[1] - boxLow*m_p[1]; + boxCorners[6] = m_p[2] - boxLow*m_p[1]; + boxCorners[7] = m_p[3] - boxLow*m_p[1]; const Vec3 boxFaces[6][4] = { { boxCorners[0], boxCorners[1], boxCorners[2], boxCorners[3] }, From 10a124825cd480f414662167d06f5e5e0833afc3 Mon Sep 17 00:00:00 2001 From: nixt Date: Sun, 8 Jun 2014 02:48:20 +0530 Subject: [PATCH 014/350] Tracking of Player kart on the quad graph. This is needed because the player kart's gravity will also be adjusted depending on which quad(track node) it is on. --- src/karts/controller/player_controller.cpp | 42 ++++++++++++++++++++++ src/karts/controller/player_controller.hpp | 4 +++ 2 files changed, 46 insertions(+) diff --git a/src/karts/controller/player_controller.cpp b/src/karts/controller/player_controller.cpp index 68927a30d..7fa84a679 100644 --- a/src/karts/controller/player_controller.cpp +++ b/src/karts/controller/player_controller.cpp @@ -38,6 +38,8 @@ #include "network/network_world.hpp" #include "race/history.hpp" #include "states_screens/race_gui_base.hpp" +#include "tracks/quad_set.hpp" +#include "tracks/quad_graph.hpp" #include "utils/constants.hpp" #include "utils/log.hpp" #include "utils/translation.hpp" @@ -94,6 +96,20 @@ void PlayerController::reset() m_prev_accel = 0; m_prev_nitro = false; m_penalty_time = 0; + + m_track_node = QuadGraph::UNKNOWN_SECTOR; + if (QuadGraph::get()) + { + QuadGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node); + if (m_track_node == QuadGraph::UNKNOWN_SECTOR) + { + Log::error("SkiddingAI", + "Invalid starting position for '%s' - not on track" + " - can be ignored.", + m_kart->getIdent().c_str()); + m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ()); + } + } } // reset // ---------------------------------------------------------------------------- @@ -307,6 +323,27 @@ void PlayerController::skidBonusTriggered() */ void PlayerController::update(float dt) { + + if (QuadGraph::get()) + { + // Update the current node: + int old_node = m_track_node; + if (m_track_node != QuadGraph::UNKNOWN_SECTOR) + { + QuadGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node); + } + // If we can't find a proper place on the track, to a broader search + // on off-track locations. + if (m_track_node == QuadGraph::UNKNOWN_SECTOR) + { + m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ()); + } + // IF the Player is off track (or on a branch of the track it did not + // select to be on), keep the old position. + if (m_track_node == QuadGraph::UNKNOWN_SECTOR) + m_track_node = old_node; + } + if (UserConfigParams::m_gamepad_debug) { // Print a dividing line so that it's easier to see which events @@ -489,3 +526,8 @@ void PlayerController::collectedItem(const Item &item, int add_info, float old_e } } } // collectedItem + +const Vec3& PlayerController::getTrackNodeNormal() +{ + return QuadSet::get()->getQuad(m_track_node).getNormal(); +} \ No newline at end of file diff --git a/src/karts/controller/player_controller.hpp b/src/karts/controller/player_controller.hpp index 1e75ac8e5..ad4339921 100644 --- a/src/karts/controller/player_controller.hpp +++ b/src/karts/controller/player_controller.hpp @@ -43,6 +43,8 @@ private: float m_penalty_time; + int m_track_node; + /** The camera attached to the kart for this controller. The camera * object is managed in the Camera class, so no need to free it. */ Camera *m_camera; @@ -81,6 +83,8 @@ public: /** Player will always be able to get a slipstream bonus. */ virtual bool disableSlipstreamBonus() const { return false; } + const Vec3& getTrackNodeNormal(); + }; // PlayerController #endif From 2e27e0065d01e46c864a84e2acf663716e92b238 Mon Sep 17 00:00:00 2001 From: nixt Date: Sun, 8 Jun 2014 03:02:16 +0530 Subject: [PATCH 015/350] New function in AIBaseController and PlayerController(previous commit) that returns the normal of the current quad. --- src/karts/controller/ai_base_controller.cpp | 5 +++++ src/karts/controller/ai_base_controller.hpp | 1 + src/tracks/quad.cpp | 3 +++ src/tracks/quad.hpp | 3 +++ 4 files changed, 12 insertions(+) diff --git a/src/karts/controller/ai_base_controller.cpp b/src/karts/controller/ai_base_controller.cpp index a192eb5ba..987165533 100644 --- a/src/karts/controller/ai_base_controller.cpp +++ b/src/karts/controller/ai_base_controller.cpp @@ -517,3 +517,8 @@ bool AIBaseController::disableSlipstreamBonus() const { return m_ai_properties->disableSlipstreamUsage(); } // disableSlipstreamBonus + +const Vec3& AIBaseController::getTrackNodeNormal() +{ + return QuadSet::get()->getQuad(m_track_node).getNormal(); +} \ No newline at end of file diff --git a/src/karts/controller/ai_base_controller.hpp b/src/karts/controller/ai_base_controller.hpp index d27143833..2e918f67c 100644 --- a/src/karts/controller/ai_base_controller.hpp +++ b/src/karts/controller/ai_base_controller.hpp @@ -115,6 +115,7 @@ public: virtual void action(PlayerAction action, int value) {}; virtual void skidBonusTriggered() {}; virtual bool disableSlipstreamBonus() const; + const Vec3& getTrackNodeNormal(); }; // AIBaseController #endif diff --git a/src/tracks/quad.cpp b/src/tracks/quad.cpp index 8535ecc74..d572295b3 100644 --- a/src/tracks/quad.cpp +++ b/src/tracks/quad.cpp @@ -52,6 +52,9 @@ Quad::Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, m_invisible = invisible; m_ai_ignore = ai_ignore; + m_normal = (p1 - p0).cross(p2 - p1); + m_normal = m_normal.normalize(); + } // Quad // ---------------------------------------------------------------------------- diff --git a/src/tracks/quad.hpp b/src/tracks/quad.hpp index 704cc1edd..3f472a67a 100644 --- a/src/tracks/quad.hpp +++ b/src/tracks/quad.hpp @@ -57,6 +57,8 @@ private: /** Set if this quad should not be used by the AI. */ bool m_ai_ignore; + Vec3 m_normal; + public: Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, bool invis=false, bool ai_ignore=false); @@ -81,5 +83,6 @@ public: /** True if this quad should be ignored by the AI. */ bool letAIIgnore() const { return m_ai_ignore; } + const Vec3& getNormal() const { return m_normal; } }; // class Quad #endif From 9e95fb4a7ae356507225cce93abc981f27952c7d Mon Sep 17 00:00:00 2001 From: nixt Date: Mon, 9 Jun 2014 17:44:39 +0530 Subject: [PATCH 016/350] Update terrain info based on start position and a direction. --- src/tracks/terrain_info.cpp | 13 +++++++++++++ src/tracks/terrain_info.hpp | 1 + 2 files changed, 14 insertions(+) diff --git a/src/tracks/terrain_info.cpp b/src/tracks/terrain_info.cpp index b12f0d9d3..33c6ccf48 100644 --- a/src/tracks/terrain_info.cpp +++ b/src/tracks/terrain_info.cpp @@ -73,6 +73,19 @@ void TerrainInfo::update(const btTransform &trans, const Vec3 &offset) const TriangleMesh &tm = World::getWorld()->getTrack()->getTriangleMesh(); tm.castRay(from, to, &m_hit_point, &m_material, &m_normal); } // update +//----------------------------------------------------------------------------- +/** Update the terrain information based on the latest position. +* \param Position from which to start the rayast from. +*/ +void TerrainInfo::update(const Vec3& from, const Vec3& towards) +{ + m_last_material = m_material; + Vec3 direction = towards.normalized(); + btVector3 to = from + 10000.0f*direction; + + const TriangleMesh &tm = World::getWorld()->getTrack()->getTriangleMesh(); + tm.castRay(from, to, &m_hit_point, &m_material, &m_normal); +} // update // ----------------------------------------------------------------------------- /** Does a raycast upwards from the given position diff --git a/src/tracks/terrain_info.hpp b/src/tracks/terrain_info.hpp index 68301718a..c7762fc38 100644 --- a/src/tracks/terrain_info.hpp +++ b/src/tracks/terrain_info.hpp @@ -49,6 +49,7 @@ public: const Material **m); virtual void update(const btTransform &trans, const Vec3 &offset); virtual void update(const Vec3 &from); + virtual void update(const Vec3& from, const Vec3& towards = Vec3(0,-1,0)); // ------------------------------------------------------------------------ /** Simple wrapper with no offset. */ From 579c26048b3b4800f10c250964f8041c182666ac Mon Sep 17 00:00:00 2001 From: nixt Date: Thu, 12 Jun 2014 04:55:21 +0530 Subject: [PATCH 017/350] Revert "Tracking of Player kart on the quad graph." This reverts commit 10a124825cd480f414662167d06f5e5e0833afc3. --- src/karts/controller/player_controller.cpp | 42 ---------------------- src/karts/controller/player_controller.hpp | 4 --- 2 files changed, 46 deletions(-) diff --git a/src/karts/controller/player_controller.cpp b/src/karts/controller/player_controller.cpp index 7fa84a679..68927a30d 100644 --- a/src/karts/controller/player_controller.cpp +++ b/src/karts/controller/player_controller.cpp @@ -38,8 +38,6 @@ #include "network/network_world.hpp" #include "race/history.hpp" #include "states_screens/race_gui_base.hpp" -#include "tracks/quad_set.hpp" -#include "tracks/quad_graph.hpp" #include "utils/constants.hpp" #include "utils/log.hpp" #include "utils/translation.hpp" @@ -96,20 +94,6 @@ void PlayerController::reset() m_prev_accel = 0; m_prev_nitro = false; m_penalty_time = 0; - - m_track_node = QuadGraph::UNKNOWN_SECTOR; - if (QuadGraph::get()) - { - QuadGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node); - if (m_track_node == QuadGraph::UNKNOWN_SECTOR) - { - Log::error("SkiddingAI", - "Invalid starting position for '%s' - not on track" - " - can be ignored.", - m_kart->getIdent().c_str()); - m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ()); - } - } } // reset // ---------------------------------------------------------------------------- @@ -323,27 +307,6 @@ void PlayerController::skidBonusTriggered() */ void PlayerController::update(float dt) { - - if (QuadGraph::get()) - { - // Update the current node: - int old_node = m_track_node; - if (m_track_node != QuadGraph::UNKNOWN_SECTOR) - { - QuadGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node); - } - // If we can't find a proper place on the track, to a broader search - // on off-track locations. - if (m_track_node == QuadGraph::UNKNOWN_SECTOR) - { - m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ()); - } - // IF the Player is off track (or on a branch of the track it did not - // select to be on), keep the old position. - if (m_track_node == QuadGraph::UNKNOWN_SECTOR) - m_track_node = old_node; - } - if (UserConfigParams::m_gamepad_debug) { // Print a dividing line so that it's easier to see which events @@ -526,8 +489,3 @@ void PlayerController::collectedItem(const Item &item, int add_info, float old_e } } } // collectedItem - -const Vec3& PlayerController::getTrackNodeNormal() -{ - return QuadSet::get()->getQuad(m_track_node).getNormal(); -} \ No newline at end of file diff --git a/src/karts/controller/player_controller.hpp b/src/karts/controller/player_controller.hpp index ad4339921..1e75ac8e5 100644 --- a/src/karts/controller/player_controller.hpp +++ b/src/karts/controller/player_controller.hpp @@ -43,8 +43,6 @@ private: float m_penalty_time; - int m_track_node; - /** The camera attached to the kart for this controller. The camera * object is managed in the Camera class, so no need to free it. */ Camera *m_camera; @@ -83,8 +81,6 @@ public: /** Player will always be able to get a slipstream bonus. */ virtual bool disableSlipstreamBonus() const { return false; } - const Vec3& getTrackNodeNormal(); - }; // PlayerController #endif From b5017dd452101ee5acf407361bbfecb2c2f19d1a Mon Sep 17 00:00:00 2001 From: nixt Date: Thu, 12 Jun 2014 04:56:10 +0530 Subject: [PATCH 018/350] Revert "New function in AIBaseController and PlayerController(previous commit) that returns the normal of the current quad." This reverts commit 2e27e0065d01e46c864a84e2acf663716e92b238. --- src/karts/controller/ai_base_controller.cpp | 5 ----- src/karts/controller/ai_base_controller.hpp | 1 - src/tracks/quad.cpp | 3 --- src/tracks/quad.hpp | 3 --- 4 files changed, 12 deletions(-) diff --git a/src/karts/controller/ai_base_controller.cpp b/src/karts/controller/ai_base_controller.cpp index 987165533..a192eb5ba 100644 --- a/src/karts/controller/ai_base_controller.cpp +++ b/src/karts/controller/ai_base_controller.cpp @@ -517,8 +517,3 @@ bool AIBaseController::disableSlipstreamBonus() const { return m_ai_properties->disableSlipstreamUsage(); } // disableSlipstreamBonus - -const Vec3& AIBaseController::getTrackNodeNormal() -{ - return QuadSet::get()->getQuad(m_track_node).getNormal(); -} \ No newline at end of file diff --git a/src/karts/controller/ai_base_controller.hpp b/src/karts/controller/ai_base_controller.hpp index 2e918f67c..d27143833 100644 --- a/src/karts/controller/ai_base_controller.hpp +++ b/src/karts/controller/ai_base_controller.hpp @@ -115,7 +115,6 @@ public: virtual void action(PlayerAction action, int value) {}; virtual void skidBonusTriggered() {}; virtual bool disableSlipstreamBonus() const; - const Vec3& getTrackNodeNormal(); }; // AIBaseController #endif diff --git a/src/tracks/quad.cpp b/src/tracks/quad.cpp index d572295b3..8535ecc74 100644 --- a/src/tracks/quad.cpp +++ b/src/tracks/quad.cpp @@ -52,9 +52,6 @@ Quad::Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, m_invisible = invisible; m_ai_ignore = ai_ignore; - m_normal = (p1 - p0).cross(p2 - p1); - m_normal = m_normal.normalize(); - } // Quad // ---------------------------------------------------------------------------- diff --git a/src/tracks/quad.hpp b/src/tracks/quad.hpp index 3f472a67a..704cc1edd 100644 --- a/src/tracks/quad.hpp +++ b/src/tracks/quad.hpp @@ -57,8 +57,6 @@ private: /** Set if this quad should not be used by the AI. */ bool m_ai_ignore; - Vec3 m_normal; - public: Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, bool invis=false, bool ai_ignore=false); @@ -83,6 +81,5 @@ public: /** True if this quad should be ignored by the AI. */ bool letAIIgnore() const { return m_ai_ignore; } - const Vec3& getNormal() const { return m_normal; } }; // class Quad #endif From f3a5677e788945ab0a47d3c0b9d039f9c9cb6dae Mon Sep 17 00:00:00 2001 From: nixt Date: Thu, 12 Jun 2014 06:23:05 +0530 Subject: [PATCH 019/350] Using quad normals to find material and set gravity. (not yet functional) --- src/karts/kart.cpp | 18 ++++++++++++------ src/tracks/quad.cpp | 3 +++ src/tracks/quad.hpp | 4 ++++ src/tracks/terrain_info.hpp | 2 +- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index a285db622..dea4d6c4c 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -410,8 +410,8 @@ void Kart::reset() m_skidmarks->adjustFog(track->isFogEnabled() ); } - - m_terrain_info->update(getTrans()); + m_terrain_info->update(getXYZ(), Vec3(0,-1,0)); + // Reset is also called when the kart is created, at which time // m_controller is not yet defined, so this has to be tested here. @@ -1214,7 +1214,11 @@ void Kart::update(float dt) m_body->getBroadphaseHandle()->m_collisionFilterGroup = 0; } - m_terrain_info->update(getTrans(), epsilon); + unsigned int sector = ((LinearWorld*)World::getWorld())->getTrackSector(getWorldKartId()).getCurrentGraphNode(); + const Vec3 quadNormal = QuadGraph::get()->getQuadOfNode(sector).getNormal(); + m_terrain_info->update(getXYZ() + epsilon*(-quadNormal), -quadNormal); + + if(m_body->getBroadphaseHandle()) { m_body->getBroadphaseHandle()->m_collisionFilterGroup = old_group; @@ -1245,10 +1249,12 @@ void Kart::update(float dt) Vec3 gravity(0.0f, -g, 0.0f); btRigidBody *body = getVehicle()->getRigidBody(); // If the material should overwrite the gravity, - if (material->hasGravity()) + if (material->hasGravity() || 1) { - Vec3 normal = m_terrain_info->getNormal(); - gravity = normal * -g; + //Vec3 normal = m_terrain_info->getNormal(); + + gravity = quadNormal * -g; + std::cout << gravity.x() << " "<< gravity.y() <<" " << gravity.z() << std::endl; } body->setGravity(gravity); } // if !flying diff --git a/src/tracks/quad.cpp b/src/tracks/quad.cpp index 8535ecc74..d572295b3 100644 --- a/src/tracks/quad.cpp +++ b/src/tracks/quad.cpp @@ -52,6 +52,9 @@ Quad::Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, m_invisible = invisible; m_ai_ignore = ai_ignore; + m_normal = (p1 - p0).cross(p2 - p1); + m_normal = m_normal.normalize(); + } // Quad // ---------------------------------------------------------------------------- diff --git a/src/tracks/quad.hpp b/src/tracks/quad.hpp index 704cc1edd..5712c5856 100644 --- a/src/tracks/quad.hpp +++ b/src/tracks/quad.hpp @@ -43,6 +43,8 @@ private: * This saves some computations at runtime. */ Vec3 m_center; + Vec3 m_normal; + /** The minimum height of the quad, used in case that several quads * are on top of each other when determining the sector a kart is on. */ float m_min_height; @@ -81,5 +83,7 @@ public: /** True if this quad should be ignored by the AI. */ bool letAIIgnore() const { return m_ai_ignore; } + const Vec3& getNormal() const { return m_normal; } + }; // class Quad #endif diff --git a/src/tracks/terrain_info.hpp b/src/tracks/terrain_info.hpp index c7762fc38..43638444e 100644 --- a/src/tracks/terrain_info.hpp +++ b/src/tracks/terrain_info.hpp @@ -49,7 +49,7 @@ public: const Material **m); virtual void update(const btTransform &trans, const Vec3 &offset); virtual void update(const Vec3 &from); - virtual void update(const Vec3& from, const Vec3& towards = Vec3(0,-1,0)); + virtual void update(const Vec3& from, const Vec3& towards); // ------------------------------------------------------------------------ /** Simple wrapper with no offset. */ From def4b2712421e4ad95d9a0ef54b0c918e623bf5d Mon Sep 17 00:00:00 2001 From: nixt Date: Thu, 12 Jun 2014 10:05:38 +0530 Subject: [PATCH 020/350] Fix inverted sign on quad normals and epsilon. --- src/karts/kart.cpp | 6 +++--- src/tracks/quad.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index dea4d6c4c..c5e19230f 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1216,7 +1216,7 @@ void Kart::update(float dt) unsigned int sector = ((LinearWorld*)World::getWorld())->getTrackSector(getWorldKartId()).getCurrentGraphNode(); const Vec3 quadNormal = QuadGraph::get()->getQuadOfNode(sector).getNormal(); - m_terrain_info->update(getXYZ() + epsilon*(-quadNormal), -quadNormal); + m_terrain_info->update(getXYZ() + epsilon*(quadNormal), -quadNormal); if(m_body->getBroadphaseHandle()) @@ -1251,9 +1251,9 @@ void Kart::update(float dt) // If the material should overwrite the gravity, if (material->hasGravity() || 1) { - //Vec3 normal = m_terrain_info->getNormal(); + Vec3 normal = m_terrain_info->getNormal(); - gravity = quadNormal * -g; + gravity = normal*-g; std::cout << gravity.x() << " "<< gravity.y() <<" " << gravity.z() << std::endl; } body->setGravity(gravity); diff --git a/src/tracks/quad.cpp b/src/tracks/quad.cpp index d572295b3..caf77e727 100644 --- a/src/tracks/quad.cpp +++ b/src/tracks/quad.cpp @@ -52,7 +52,7 @@ Quad::Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, m_invisible = invisible; m_ai_ignore = ai_ignore; - m_normal = (p1 - p0).cross(p2 - p1); + m_normal = (p2 - p1).cross(p1 - p0); m_normal = m_normal.normalize(); } // Quad From 94481097b6b6fc05f1b5a780863e380ba05b5885 Mon Sep 17 00:00:00 2001 From: nixt Date: Thu, 12 Jun 2014 10:08:02 +0530 Subject: [PATCH 021/350] Temporary workaround to make track debug work. --- src/graphics/irr_driver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 2f4291056..9024415a2 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -444,8 +444,8 @@ void IrrDriver::initDevice() glGetIntegerv(GL_MINOR_VERSION, &GLMinorVersion); } Log::info("IrrDriver", "OPENGL VERSION IS %d.%d", GLMajorVersion, GLMinorVersion); - m_glsl = (GLMajorVersion > 3 || (GLMajorVersion == 3 && GLMinorVersion >= 1)); - + //m_glsl = (GLMajorVersion > 3 || (GLMajorVersion == 3 && GLMinorVersion >= 1)); + m_glsl = false; // Parse extensions hasVSLayer = false; // Default false value for hasVSLayer if --no-graphics argument is used From 4ce486192524bfb88568e53ce314152bcdf5ad5f Mon Sep 17 00:00:00 2001 From: nixt Date: Sat, 14 Jun 2014 02:52:29 +0530 Subject: [PATCH 022/350] Do not reset gravity when kart is flying. Simply commented out setGravity(), will do a proper cleanup once fully tested. --- src/karts/kart.cpp | 2 +- src/tracks/graph_node.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index c5e19230f..bc0e72457 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1232,7 +1232,7 @@ void Kart::update(float dt) float g = World::getWorld()->getTrack()->getGravity(); Vec3 gravity(0, -g, 0); btRigidBody *body = getVehicle()->getRigidBody(); - body->setGravity(gravity); + //body->setGravity(gravity); } // let kart fall a bit before rescuing const Vec3 *min, *max; diff --git a/src/tracks/graph_node.cpp b/src/tracks/graph_node.cpp index e43eeb988..cbe5cb1de 100644 --- a/src/tracks/graph_node.cpp +++ b/src/tracks/graph_node.cpp @@ -16,7 +16,7 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, B -#include "tracks/quad_graph.hpp" +#include "tracks/graph_node.hpp" #include "io/file_manager.hpp" #include "io/xml_node.hpp" From 64b21b0fff8f264b31fd9b034b16f027e6cf5ce9 Mon Sep 17 00:00:00 2001 From: nixt Date: Sat, 14 Jun 2014 02:53:20 +0530 Subject: [PATCH 023/350] Flattening of quads. Each quad now has a n associated flattened (rotated) quad which is more or less in the XZ plane. --- src/tracks/quad.cpp | 31 ++++++++++++++++++++++++++++--- src/tracks/quad.hpp | 6 ++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/tracks/quad.cpp b/src/tracks/quad.cpp index caf77e727..4397bf7b7 100644 --- a/src/tracks/quad.cpp +++ b/src/tracks/quad.cpp @@ -19,6 +19,7 @@ #include "tracks/quad.hpp" #include +#include #include #include @@ -52,9 +53,8 @@ Quad::Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, m_invisible = invisible; m_ai_ignore = ai_ignore; - m_normal = (p2 - p1).cross(p1 - p0); - m_normal = m_normal.normalize(); - + findNormal(); + } // Quad // ---------------------------------------------------------------------------- @@ -176,3 +176,28 @@ void Quad::transform(const btTransform &t, Quad *result) const result->m_p[3].getY()) ); } // transform +void Quad::findNormal() +{ + core::triangle3df tri1(m_p[0].toIrrVector(), m_p[1].toIrrVector(), m_p[2].toIrrVector()); + core::triangle3df tri2(m_p[0].toIrrVector(), m_p[2].toIrrVector(), m_p[3].toIrrVector()); + Vec3 normal1 = tri1.getNormal(); + Vec3 normal2 = tri2.getNormal(); + m_normal = -0.5f*(normal1 + normal2); + m_normal.normalize(); +} + +void Quad::findFlattenedQuads() +{ + core::CMatrix4 m; + m.buildRotateFromTo(m_normal.toIrrVector(), core::vector3df(0, 1, 0)); + + for (unsigned int i = 0; i < 4; i++) + { + // Translate to origin + m_p_flat[i] = m_p[i] - m_center; + + // rotateVect(out,in) + m.rotateVect(m_p_flat[i] , m_p_flat[i].toIrrVector()); + } + +} diff --git a/src/tracks/quad.hpp b/src/tracks/quad.hpp index 5712c5856..251b638d9 100644 --- a/src/tracks/quad.hpp +++ b/src/tracks/quad.hpp @@ -39,6 +39,8 @@ private: /** The four points of a quad. */ Vec3 m_p[4]; + Vec3 m_p_flat[4]; + /** The center of all four points, which is used by the AI. * This saves some computations at runtime. */ Vec3 m_center; @@ -59,6 +61,10 @@ private: /** Set if this quad should not be used by the AI. */ bool m_ai_ignore; + void findNormal(); + + void findFlattenedQuads(); + public: Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, bool invis=false, bool ai_ignore=false); From 306e85646463fd7087c24fce87f120bb4c66b40b Mon Sep 17 00:00:00 2001 From: nixt Date: Mon, 16 Jun 2014 10:05:37 +0530 Subject: [PATCH 024/350] Build a vector of unrolled quads for each graph node. --- src/tracks/graph_node.cpp | 50 +++++++++++++++++++++++++++++++++++++++ src/tracks/graph_node.hpp | 5 ++++ src/tracks/quad.cpp | 9 ++++--- src/tracks/quad.hpp | 8 +++---- 4 files changed, 64 insertions(+), 8 deletions(-) diff --git a/src/tracks/graph_node.cpp b/src/tracks/graph_node.cpp index cbe5cb1de..a9f119486 100644 --- a/src/tracks/graph_node.cpp +++ b/src/tracks/graph_node.cpp @@ -20,6 +20,7 @@ #include "io/file_manager.hpp" #include "io/xml_node.hpp" +#include "matrix4.h" #include "tracks/quad_graph.hpp" #include "tracks/quad_set.hpp" @@ -216,3 +217,52 @@ void GraphNode::setChecklineRequirements(int latest_checkline) { m_checkline_requirements.push_back(latest_checkline); } + +void GraphNode::buildUnrolledQuads() +{ + m_unrolled_quads.clear(); + + Quad thisQuad = getQuad(); + + m_unrolled_quads.push_back(thisQuad.findFlattenedQuads()); + + int k = 15; + //unsigned int successorCount = getNumberOfSuccessors(); + addUnrolledQuad(QuadGraph::get()->getNode(getSuccessor(0)), k); + +} + +void GraphNode::addUnrolledQuad(const GraphNode& next_node, int k) +{ + if (k == 0) return; + + Quad next_quad = next_node.getQuad(); + Quad next_quad_to_push = next_quad.findFlattenedQuads(); + Quad last_pushed_quad = m_unrolled_quads.back(); + + Vec3 endEdge = last_pushed_quad[2] - last_pushed_quad[3]; + Vec3 beginEdge = next_quad_to_push[1] - next_quad_to_push[0]; + + core::CMatrix4 m; + core::vector3df new_points[4]; + + // First rotate the next_quad_to_push so that it aligns with last quad + // in the vector of unrolled quads + m.buildRotateFromTo(beginEdge.toIrrVector(), endEdge.toIrrVector()); + for (int i = 0; i < 4; i++) + m.rotateVect(new_points[i], next_quad_to_push[i].toIrrVector()); + + // Next translate the new quad to be pushed to the correct position infront + // of the last quad in the vector of unrolled quads + Vec3 lower_center = 0.5f*(last_pushed_quad[0] + last_pushed_quad[1]); + Vec3 upper_center = 0.5f*(next_quad_to_push[0] + next_quad_to_push[1]); + m.setTranslation((lower_center - upper_center).toIrrVector()); + for (int i = 0; i < 4; i++) + m.translateVect((core::vector3df)new_points[i]); + + // Push the quad into the vector of unrolled quads + m_unrolled_quads.push_back(Quad(new_points[0], new_points[1], new_points[2], new_points[3])); + + // Recurisvely build the vector of unrolled quads till k reduces to 0 + addUnrolledQuad(QuadGraph::get()->getNode(next_node.getSuccessor(0)), k--); +} \ No newline at end of file diff --git a/src/tracks/graph_node.hpp b/src/tracks/graph_node.hpp index 43d1610f5..4a26c7568 100644 --- a/src/tracks/graph_node.hpp +++ b/src/tracks/graph_node.hpp @@ -124,9 +124,14 @@ private: */ std::vector< int > m_checkline_requirements; + std::vector m_unrolled_quads; + void markAllSuccessorsToUse(unsigned int n, PathToNodeVector *m_path_to_node); + void buildUnrolledQuads(); + void GraphNode::addUnrolledQuad(const GraphNode& next_node,int k); + public: GraphNode(unsigned int quad_index, unsigned int node_index); void addSuccessor (unsigned int to); diff --git a/src/tracks/quad.cpp b/src/tracks/quad.cpp index 4397bf7b7..5e0e6e130 100644 --- a/src/tracks/quad.cpp +++ b/src/tracks/quad.cpp @@ -186,11 +186,11 @@ void Quad::findNormal() m_normal.normalize(); } -void Quad::findFlattenedQuads() +Quad Quad::findFlattenedQuads() { core::CMatrix4 m; m.buildRotateFromTo(m_normal.toIrrVector(), core::vector3df(0, 1, 0)); - + Vec3 m_p_flat[4]; for (unsigned int i = 0; i < 4; i++) { // Translate to origin @@ -198,6 +198,9 @@ void Quad::findFlattenedQuads() // rotateVect(out,in) m.rotateVect(m_p_flat[i] , m_p_flat[i].toIrrVector()); + + } - + + return Quad(m_p_flat[0], m_p_flat[1], m_p_flat[2], m_p_flat[3]); } diff --git a/src/tracks/quad.hpp b/src/tracks/quad.hpp index 251b638d9..8e4c61362 100644 --- a/src/tracks/quad.hpp +++ b/src/tracks/quad.hpp @@ -39,8 +39,6 @@ private: /** The four points of a quad. */ Vec3 m_p[4]; - Vec3 m_p_flat[4]; - /** The center of all four points, which is used by the AI. * This saves some computations at runtime. */ Vec3 m_center; @@ -62,9 +60,7 @@ private: bool m_ai_ignore; void findNormal(); - - void findFlattenedQuads(); - + public: Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, bool invis=false, bool ai_ignore=false); @@ -91,5 +87,7 @@ public: const Vec3& getNormal() const { return m_normal; } + Quad findFlattenedQuads(); + }; // class Quad #endif From 40f205dd0f5285ba2a0a47bed0438f973d947296 Mon Sep 17 00:00:00 2001 From: nixt Date: Mon, 16 Jun 2014 12:14:38 +0530 Subject: [PATCH 025/350] Function to get a point transformed to the reference of the flattened quad --- src/tracks/graph_node.cpp | 13 +++++++++++++ src/tracks/graph_node.hpp | 2 ++ 2 files changed, 15 insertions(+) diff --git a/src/tracks/graph_node.cpp b/src/tracks/graph_node.cpp index a9f119486..aee5f02e6 100644 --- a/src/tracks/graph_node.cpp +++ b/src/tracks/graph_node.cpp @@ -232,6 +232,19 @@ void GraphNode::buildUnrolledQuads() } +const Vec3 GraphNode::getPointTransformedToFlatQuad(Vec3 xyz) +{ + Quad thisQuad = getQuad(); + core::CMatrix4 m; + m.buildRotateFromTo(thisQuad.getNormal().toIrrVector(), core::vector3df(0, 1, 0)); + + core::vector3df result; + // Translate the input point into the Quads frame of reference, then rotate + m.rotateVect(result, (xyz - thisQuad.getCenter()).toIrrVector()); + + return (Vec3)result; +} + void GraphNode::addUnrolledQuad(const GraphNode& next_node, int k) { if (k == 0) return; diff --git a/src/tracks/graph_node.hpp b/src/tracks/graph_node.hpp index 4a26c7568..7a6c665db 100644 --- a/src/tracks/graph_node.hpp +++ b/src/tracks/graph_node.hpp @@ -237,6 +237,8 @@ public: // ------------------------------------------------------------------------ /** Returns a unit vector pointing to the right side of the quad. */ const Vec3 &getRightUnitVector() const { return m_right_unit_vector; } + + const Vec3 getPointTransformedToFlatQuad(Vec3 xyz); }; // GraphNode #endif From 39f76b02ff99916b28b28892f7e19d60fc6d1306 Mon Sep 17 00:00:00 2001 From: nixt Date: Mon, 16 Jun 2014 14:38:03 +0530 Subject: [PATCH 026/350] Temporary function to create a debug mesh for unrolled quads --- src/tracks/quad_graph.cpp | 92 +++++++++++++++++++++++++++++++++++++-- src/tracks/quad_graph.hpp | 1 + 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/src/tracks/quad_graph.cpp b/src/tracks/quad_graph.cpp index 6d343b7eb..a0c07a971 100644 --- a/src/tracks/quad_graph.cpp +++ b/src/tracks/quad_graph.cpp @@ -201,6 +201,11 @@ void QuadGraph::load(const std::string &filename) if(l > m_lap_length) m_lap_length = l; } + + for (unsigned int i = 0; i < m_all_nodes.size(); i++) + { + m_all_nodes[i]->buildUnrolledQuads(); + } } // load // ---------------------------------------------------------------------------- @@ -525,6 +530,87 @@ void QuadGraph::createMesh(bool show_invisible, delete[] new_v; } // createMesh +// ----------------------------------------------------------------------------- +/** Creates the actual mesh that is used by createDebugMesh() */ +void QuadGraph::createMesh2() +{ + // The debug track will not be lighted or culled. + video::SMaterial m; + m.BackfaceCulling = false; + m.Lighting = false; + + m.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + m_mesh = irr_driver->createQuadMesh(&m); + m_mesh_buffer = m_mesh->getMeshBuffer(0); + assert(m_mesh_buffer->getVertexType() == video::EVT_STANDARD); + + + // Eps is used to raise the track debug quads a little bit higher than + // the ground, so that they are actually visible. + core::vector3df eps(0, 0.4f, 0); + video::SColor defaultColor(255, 255, 0, 0), c; + + // Declare vector to hold new converted vertices, vertices are copied over + // for each polygon, although it results in redundant vertex copies in the + // final vector, this is the only way I know to make each poly have different color. + + + int n_unrolled_quads = 9; + // Declare vector to hold indices + irr::u16 *ind = new irr::u16[6 * n_unrolled_quads]; + video::S3DVertex *new_v = new video::S3DVertex[4 * n_unrolled_quads]; + int i = 0; + for (unsigned int count = 0; count< n_unrolled_quads; count++) + { + ///compute colors + + { + c.setAlpha(178); + //c.setRed ((i%2) ? 255 : 0); + //c.setBlue((i%3) ? 0 : 255); + c.setRed(7 * i % 256); + c.setBlue((2 * i) % 256); + c.setGreen((3 * i) % 256); + } + + Quad flatquad = QuadGraph::get()->getNode(0).getUnrolledQuad(count); + + //std::vector vInd = poly.getVerticesIndex(); + // Four vertices for each of the n-1 remaining quads + + flatquad.getVertices(new_v + 4*count,c); + + // Number of triangles in the triangle fan + unsigned int numberOfTriangles = 2; + + // Set up the indices for the triangles + + + + + ind[6 * i] = 4 * i; // First triangle: vertex 0, 1, 2 + ind[6 * i + 1] = 4 * i + 1; + ind[6 * i + 2] = 4 * i + 2; + ind[6 * i + 3] = 4 * i; // second triangle: vertex 0, 1, 3 + ind[6 * i + 4] = 4 * i + 2; + ind[6 * i + 5] = 4 * i + 3; + i++; + + + } + + m_mesh_buffer->append(new_v, n_unrolled_quads*4, ind, n_unrolled_quads*6); + + // Instead of setting the bounding boxes, we could just disable culling, + // since the debug track should always be drawn. + //m_node->setAutomaticCulling(scene::EAC_OFF); + m_mesh_buffer->recalculateBoundingBox(); + m_mesh->setBoundingBox(m_mesh_buffer->getBoundingBox()); + +} // createMesh + + + // ----------------------------------------------------------------------------- /** Creates the debug mesh to display the quad graph on top of the track @@ -533,9 +619,9 @@ void QuadGraph::createDebugMesh() { if(m_all_nodes.size()<=0) return; // no debug output if not graph - createMesh(/*show_invisible*/true, - /*enable_transparency*/true); - + //createMesh(/*show_invisible*/true, + // /*enable_transparency*/true); + createMesh2(); // Now colour the quads red/blue/red ... video::SColor c( 128, 255, 0, 0); video::S3DVertex *v = (video::S3DVertex*)m_mesh_buffer->getVertices(); diff --git a/src/tracks/quad_graph.hpp b/src/tracks/quad_graph.hpp index e4e58b260..3ddc9bdff 100644 --- a/src/tracks/quad_graph.hpp +++ b/src/tracks/quad_graph.hpp @@ -91,6 +91,7 @@ private: bool enable_transparency=false, const video::SColor *track_color=NULL, const video::SColor *lap_color=NULL); + void createMesh2(); unsigned int getStartNode() const; QuadGraph (const std::string &quad_file_name, const std::string graph_file_name, From ce0a3db19521c6921bdc78f42e6849857e867c3d Mon Sep 17 00:00:00 2001 From: nixt Date: Mon, 16 Jun 2014 17:03:31 +0530 Subject: [PATCH 027/350] Fix unrolled quads --- src/tracks/graph_node.cpp | 43 +++++++++++++++++++++------------------ src/tracks/graph_node.hpp | 4 +++- src/tracks/quad_graph.cpp | 3 ++- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/src/tracks/graph_node.cpp b/src/tracks/graph_node.cpp index aee5f02e6..813913d85 100644 --- a/src/tracks/graph_node.cpp +++ b/src/tracks/graph_node.cpp @@ -67,7 +67,7 @@ GraphNode::GraphNode(unsigned int quad_index, unsigned int node_index) // Only this 2d point is needed later m_lower_center_2d = core::vector2df(m_lower_center.getX(), m_lower_center.getZ() ); - + } // GraphNode // ---------------------------------------------------------------------------- @@ -218,19 +218,6 @@ void GraphNode::setChecklineRequirements(int latest_checkline) m_checkline_requirements.push_back(latest_checkline); } -void GraphNode::buildUnrolledQuads() -{ - m_unrolled_quads.clear(); - - Quad thisQuad = getQuad(); - - m_unrolled_quads.push_back(thisQuad.findFlattenedQuads()); - - int k = 15; - //unsigned int successorCount = getNumberOfSuccessors(); - addUnrolledQuad(QuadGraph::get()->getNode(getSuccessor(0)), k); - -} const Vec3 GraphNode::getPointTransformedToFlatQuad(Vec3 xyz) { @@ -245,6 +232,22 @@ const Vec3 GraphNode::getPointTransformedToFlatQuad(Vec3 xyz) return (Vec3)result; } + +void GraphNode::buildUnrolledQuads() +{ + m_unrolled_quads.clear(); + + Quad thisQuad = getQuad(); + + m_unrolled_quads.push_back(thisQuad); + + int k = 10; + //unsigned int successorCount = getNumberOfSuccessors(); + addUnrolledQuad(QuadGraph::get()->getNode(getSuccessor(0)), k); + +} + + void GraphNode::addUnrolledQuad(const GraphNode& next_node, int k) { if (k == 0) return; @@ -267,15 +270,15 @@ void GraphNode::addUnrolledQuad(const GraphNode& next_node, int k) // Next translate the new quad to be pushed to the correct position infront // of the last quad in the vector of unrolled quads - Vec3 lower_center = 0.5f*(last_pushed_quad[0] + last_pushed_quad[1]); - Vec3 upper_center = 0.5f*(next_quad_to_push[0] + next_quad_to_push[1]); - m.setTranslation((lower_center - upper_center).toIrrVector()); + Vec3 upper_center = 0.5f*(last_pushed_quad[2] + last_pushed_quad[3]); + Vec3 lower_center = 0.5f*(next_quad_to_push[0] + next_quad_to_push[1]); + m.setTranslation((upper_center-lower_center).toIrrVector()); for (int i = 0; i < 4; i++) - m.translateVect((core::vector3df)new_points[i]); + m.translateVect(new_points[i]); // Push the quad into the vector of unrolled quads m_unrolled_quads.push_back(Quad(new_points[0], new_points[1], new_points[2], new_points[3])); - + k = k - 1; // Recurisvely build the vector of unrolled quads till k reduces to 0 - addUnrolledQuad(QuadGraph::get()->getNode(next_node.getSuccessor(0)), k--); + addUnrolledQuad(QuadGraph::get()->getNode(next_node.getSuccessor(0)), k); } \ No newline at end of file diff --git a/src/tracks/graph_node.hpp b/src/tracks/graph_node.hpp index 7a6c665db..f98ed9db9 100644 --- a/src/tracks/graph_node.hpp +++ b/src/tracks/graph_node.hpp @@ -129,7 +129,6 @@ private: void markAllSuccessorsToUse(unsigned int n, PathToNodeVector *m_path_to_node); - void buildUnrolledQuads(); void GraphNode::addUnrolledQuad(const GraphNode& next_node,int k); public: @@ -141,6 +140,7 @@ public: void setChecklineRequirements(int latest_checkline); void setDirectionData(unsigned int successor, DirectionType dir, unsigned int last_node_index); + void buildUnrolledQuads(); // ------------------------------------------------------------------------ /** Returns the number of successors. */ unsigned int getNumberOfSuccessors() const @@ -239,6 +239,8 @@ public: const Vec3 &getRightUnitVector() const { return m_right_unit_vector; } const Vec3 getPointTransformedToFlatQuad(Vec3 xyz); + + const Quad& getUnrolledQuad(int i) const { return m_unrolled_quads[i]; } }; // GraphNode #endif diff --git a/src/tracks/quad_graph.cpp b/src/tracks/quad_graph.cpp index a0c07a971..05ced3c73 100644 --- a/src/tracks/quad_graph.cpp +++ b/src/tracks/quad_graph.cpp @@ -573,7 +573,7 @@ void QuadGraph::createMesh2() c.setGreen((3 * i) % 256); } - Quad flatquad = QuadGraph::get()->getNode(0).getUnrolledQuad(count); + Quad flatquad = QuadGraph::get()->getNode(50).getUnrolledQuad(count); //std::vector vInd = poly.getVerticesIndex(); // Four vertices for each of the n-1 remaining quads @@ -622,6 +622,7 @@ void QuadGraph::createDebugMesh() //createMesh(/*show_invisible*/true, // /*enable_transparency*/true); createMesh2(); + // Now colour the quads red/blue/red ... video::SColor c( 128, 255, 0, 0); video::S3DVertex *v = (video::S3DVertex*)m_mesh_buffer->getVertices(); From 374b8753b45ddea7f37318d1a42465f475e95424 Mon Sep 17 00:00:00 2001 From: nixt Date: Mon, 16 Jun 2014 17:04:16 +0530 Subject: [PATCH 028/350] Drop the Y (set to 0) value for flattened quads --- src/tracks/quad.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tracks/quad.cpp b/src/tracks/quad.cpp index 5e0e6e130..9a12015f5 100644 --- a/src/tracks/quad.cpp +++ b/src/tracks/quad.cpp @@ -199,7 +199,7 @@ Quad Quad::findFlattenedQuads() // rotateVect(out,in) m.rotateVect(m_p_flat[i] , m_p_flat[i].toIrrVector()); - + m_p_flat[i].setY(0); } return Quad(m_p_flat[0], m_p_flat[1], m_p_flat[2], m_p_flat[3]); From 886345b81829677e1f18503260034f1492754b00 Mon Sep 17 00:00:00 2001 From: nixt Date: Tue, 17 Jun 2014 12:44:42 +0530 Subject: [PATCH 029/350] Unrolled quads are now almost correct. After continuous trial and error, dropped using the flattened quads as it was causing the quads to shear (not sure why). Now unrolled quads have orientation in 3D starting from the parent quad. --- src/tracks/graph_node.cpp | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/tracks/graph_node.cpp b/src/tracks/graph_node.cpp index 813913d85..6dc09759a 100644 --- a/src/tracks/graph_node.cpp +++ b/src/tracks/graph_node.cpp @@ -253,29 +253,41 @@ void GraphNode::addUnrolledQuad(const GraphNode& next_node, int k) if (k == 0) return; Quad next_quad = next_node.getQuad(); - Quad next_quad_to_push = next_quad.findFlattenedQuads(); + Quad next_quad_to_push = next_quad; Quad last_pushed_quad = m_unrolled_quads.back(); - Vec3 endEdge = last_pushed_quad[2] - last_pushed_quad[3]; - Vec3 beginEdge = next_quad_to_push[1] - next_quad_to_push[0]; core::CMatrix4 m; core::vector3df new_points[4]; // First rotate the next_quad_to_push so that it aligns with last quad // in the vector of unrolled quads - m.buildRotateFromTo(beginEdge.toIrrVector(), endEdge.toIrrVector()); - for (int i = 0; i < 4; i++) - m.rotateVect(new_points[i], next_quad_to_push[i].toIrrVector()); + m.buildRotateFromTo(next_quad_to_push.getNormal().toIrrVector(), + last_pushed_quad.getNormal().toIrrVector()); + + + //m.setRotationCenter(next_quad_to_push.getCenter().toIrrVector(), + // ((next_quad_to_push.getCenter() - 0.5f*(next_quad_to_push[0] + next_quad_to_push[1])) + 0.5f*(last_pushed_quad[2] + last_pushed_quad[3))); + for (unsigned int i = 0; i < 4; i++) + m.rotateVect(new_points[i], next_quad_to_push[i].toIrrVector()); + + Vec3 endEdge = 0.5f*(last_pushed_quad[2] + last_pushed_quad[1]) - 0.5f*(last_pushed_quad[3] + last_pushed_quad[0]); + Vec3 beginEdge = 0.5f*(new_points[1] + new_points[2]) - 0.5f*(new_points[0] + new_points[3]); + /* + m.buildRotateFromTo(beginEdge.toIrrVector(), endEdge.toIrrVector()); + for (unsigned int i = 0; i < 4; i++) + m.rotateVect(new_points[i]); + */ // Next translate the new quad to be pushed to the correct position infront // of the last quad in the vector of unrolled quads + Vec3 lower_center = 0.5f*(new_points[0] + new_points[1]); Vec3 upper_center = 0.5f*(last_pushed_quad[2] + last_pushed_quad[3]); - Vec3 lower_center = 0.5f*(next_quad_to_push[0] + next_quad_to_push[1]); m.setTranslation((upper_center-lower_center).toIrrVector()); - for (int i = 0; i < 4; i++) + //m.setTranslation((last_pushed_quad[3] - next_quad_to_push[0]).toIrrVector()); + for (unsigned int i = 0; i < 4; i++) m.translateVect(new_points[i]); - + // Push the quad into the vector of unrolled quads m_unrolled_quads.push_back(Quad(new_points[0], new_points[1], new_points[2], new_points[3])); k = k - 1; From 7acfec7866081882dbc464f6a84283ffa95c66c8 Mon Sep 17 00:00:00 2001 From: nixt Date: Tue, 17 Jun 2014 13:29:39 +0530 Subject: [PATCH 030/350] Some utility functions. --- src/tracks/graph_node.cpp | 6 ++---- src/tracks/graph_node.hpp | 2 +- src/tracks/quad_graph.cpp | 28 +++++++++++++++++++++------- src/tracks/quad_graph.hpp | 10 ++++++++++ 4 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/tracks/graph_node.cpp b/src/tracks/graph_node.cpp index 6dc09759a..bac26bb2b 100644 --- a/src/tracks/graph_node.cpp +++ b/src/tracks/graph_node.cpp @@ -233,7 +233,7 @@ const Vec3 GraphNode::getPointTransformedToFlatQuad(Vec3 xyz) } -void GraphNode::buildUnrolledQuads() +void GraphNode::buildUnrolledQuads(unsigned int unroll_quad_count) { m_unrolled_quads.clear(); @@ -241,9 +241,8 @@ void GraphNode::buildUnrolledQuads() m_unrolled_quads.push_back(thisQuad); - int k = 10; //unsigned int successorCount = getNumberOfSuccessors(); - addUnrolledQuad(QuadGraph::get()->getNode(getSuccessor(0)), k); + addUnrolledQuad(QuadGraph::get()->getNode(getSuccessor(0)), unroll_quad_count); } @@ -266,7 +265,6 @@ void GraphNode::addUnrolledQuad(const GraphNode& next_node, int k) last_pushed_quad.getNormal().toIrrVector()); - //m.setRotationCenter(next_quad_to_push.getCenter().toIrrVector(), // ((next_quad_to_push.getCenter() - 0.5f*(next_quad_to_push[0] + next_quad_to_push[1])) + 0.5f*(last_pushed_quad[2] + last_pushed_quad[3))); for (unsigned int i = 0; i < 4; i++) diff --git a/src/tracks/graph_node.hpp b/src/tracks/graph_node.hpp index f98ed9db9..4d8875ab7 100644 --- a/src/tracks/graph_node.hpp +++ b/src/tracks/graph_node.hpp @@ -140,7 +140,7 @@ public: void setChecklineRequirements(int latest_checkline); void setDirectionData(unsigned int successor, DirectionType dir, unsigned int last_node_index); - void buildUnrolledQuads(); + void buildUnrolledQuads(unsigned int unroll_quad_count); // ------------------------------------------------------------------------ /** Returns the number of successors. */ unsigned int getNumberOfSuccessors() const diff --git a/src/tracks/quad_graph.cpp b/src/tracks/quad_graph.cpp index 05ced3c73..e724641a4 100644 --- a/src/tracks/quad_graph.cpp +++ b/src/tracks/quad_graph.cpp @@ -54,6 +54,7 @@ QuadGraph::QuadGraph(const std::string &quad_file_name, m_mesh = NULL; m_mesh_buffer = NULL; m_lap_length = 0; + m_unroll_quad_count = 10; QuadSet::create(); QuadSet::get()->init(quad_file_name); m_quad_filename = quad_file_name; @@ -202,9 +203,10 @@ void QuadGraph::load(const std::string &filename) m_lap_length = l; } + // Build unrolled quads for (unsigned int i = 0; i < m_all_nodes.size(); i++) { - m_all_nodes[i]->buildUnrolledQuads(); + m_all_nodes[i]->buildUnrolledQuads(m_unroll_quad_count); } } // load @@ -427,7 +429,7 @@ void QuadGraph::createMesh(bool show_invisible, if(show_invisible || !m_all_nodes[i]->getQuad().isInvisible()) n++; } - + // Four vertices for each of the n-1 remaining quads video::S3DVertex *new_v = new video::S3DVertex[4*n]; // Each quad consists of 2 triangles with 3 elements, so @@ -443,6 +445,7 @@ void QuadGraph::createMesh(bool show_invisible, for(unsigned int count=0; countgetQuad().isInvisible()) continue; // Swap the colours from red to blue and back @@ -555,7 +558,7 @@ void QuadGraph::createMesh2() // final vector, this is the only way I know to make each poly have different color. - int n_unrolled_quads = 9; + int n_unrolled_quads = getNumberOfUnrolledQuads(); // Declare vector to hold indices irr::u16 *ind = new irr::u16[6 * n_unrolled_quads]; video::S3DVertex *new_v = new video::S3DVertex[4 * n_unrolled_quads]; @@ -573,7 +576,7 @@ void QuadGraph::createMesh2() c.setGreen((3 * i) % 256); } - Quad flatquad = QuadGraph::get()->getNode(50).getUnrolledQuad(count); + Quad flatquad = QuadGraph::get()->getNode(60).getUnrolledQuad(count); //std::vector vInd = poly.getVerticesIndex(); // Four vertices for each of the n-1 remaining quads @@ -619,9 +622,9 @@ void QuadGraph::createDebugMesh() { if(m_all_nodes.size()<=0) return; // no debug output if not graph - //createMesh(/*show_invisible*/true, - // /*enable_transparency*/true); - createMesh2(); + createMesh(/*show_invisible*/true, + /*enable_transparency*/true); + // Now colour the quads red/blue/red ... video::SColor c( 128, 255, 0, 0); @@ -637,6 +640,17 @@ void QuadGraph::createDebugMesh() #ifdef DEBUG m_node->setName("track-debug-mesh"); #endif + createMesh2(); + // Now colour the quads red/blue/red ... + v = (video::S3DVertex*)m_mesh_buffer->getVertices(); + for (unsigned int i = 0; igetVertexCount(); i++) + { + // Swap the colours from red to blue and back + c.setRed((i % 2) ? 255 : 0); + c.setBlue((i % 2) ? 0 : 255); + v[i].Color = c; + } + m_node = irr_driver->addMesh(m_mesh); } // createDebugMesh diff --git a/src/tracks/quad_graph.hpp b/src/tracks/quad_graph.hpp index 3ddc9bdff..ce15199df 100644 --- a/src/tracks/quad_graph.hpp +++ b/src/tracks/quad_graph.hpp @@ -78,6 +78,8 @@ private: /** Wether the graph should be reverted or not */ bool m_reverse; + unsigned int m_unroll_quad_count; + void setDefaultSuccessors(); void computeChecklineRequirements(GraphNode* node, int latest_checkline); void computeDirectionData(); @@ -91,6 +93,7 @@ private: bool enable_transparency=false, const video::SColor *track_color=NULL, const video::SColor *lap_color=NULL); + // Temporary function for debugging unrolled quads void createMesh2(); unsigned int getStartNode() const; QuadGraph (const std::string &quad_file_name, @@ -192,6 +195,13 @@ public: /** Returns true if the graph is to be reversed. */ bool isReverse() const {return m_reverse; } + const Quad& getUnrolledQuadOfNode(unsigned int node, unsigned int quad_number) + { return getNode(node).getUnrolledQuad(quad_number); } + + /** Returns the number of forward quads that are unrolled for each quad **/ + int getNumberOfUnrolledQuads() const { return m_unroll_count; } + + }; // QuadGraph #endif From 46638c0d83eb1c6e725ae74f21c4f6e5ec12f96a Mon Sep 17 00:00:00 2001 From: nixt Date: Wed, 18 Jun 2014 08:42:24 +0530 Subject: [PATCH 031/350] Change function name to an appropriate one. --- src/tracks/quad.cpp | 2 +- src/tracks/quad.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tracks/quad.cpp b/src/tracks/quad.cpp index 9a12015f5..90abfd5e6 100644 --- a/src/tracks/quad.cpp +++ b/src/tracks/quad.cpp @@ -186,7 +186,7 @@ void Quad::findNormal() m_normal.normalize(); } -Quad Quad::findFlattenedQuads() +Quad Quad::getFlattenedQuad() { core::CMatrix4 m; m.buildRotateFromTo(m_normal.toIrrVector(), core::vector3df(0, 1, 0)); diff --git a/src/tracks/quad.hpp b/src/tracks/quad.hpp index 8e4c61362..914480578 100644 --- a/src/tracks/quad.hpp +++ b/src/tracks/quad.hpp @@ -87,7 +87,7 @@ public: const Vec3& getNormal() const { return m_normal; } - Quad findFlattenedQuads(); + Quad getFlattenedQuad(); }; // class Quad #endif From c3eae006f9958be8186cc04b1af314d8e5dfbffc Mon Sep 17 00:00:00 2001 From: nixt Date: Wed, 18 Jun 2014 08:44:06 +0530 Subject: [PATCH 032/350] New function spatialToTrackUnrolled to track points on the unrolled quads. --- src/karts/kart.cpp | 2 +- src/tracks/quad_graph.cpp | 13 ++++++++++++- src/tracks/quad_graph.hpp | 7 ++++++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index bc0e72457..d8170fc26 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1254,7 +1254,7 @@ void Kart::update(float dt) Vec3 normal = m_terrain_info->getNormal(); gravity = normal*-g; - std::cout << gravity.x() << " "<< gravity.y() <<" " << gravity.z() << std::endl; + //std::cout << gravity.x() << " "<< gravity.y() <<" " << gravity.z() << std::endl; } body->setGravity(gravity); } // if !flying diff --git a/src/tracks/quad_graph.cpp b/src/tracks/quad_graph.cpp index e724641a4..38a26e931 100644 --- a/src/tracks/quad_graph.cpp +++ b/src/tracks/quad_graph.cpp @@ -576,7 +576,7 @@ void QuadGraph::createMesh2() c.setGreen((3 * i) % 256); } - Quad flatquad = QuadGraph::get()->getNode(60).getUnrolledQuad(count); + Quad flatquad = QuadGraph::get()->getNode(28).getUnrolledQuad(count); //std::vector vInd = poly.getVerticesIndex(); // Four vertices for each of the n-1 remaining quads @@ -902,6 +902,17 @@ void QuadGraph::spatialToTrack(Vec3 *dst, const Vec3& xyz, getNode(sector).getDistances(xyz, dst); } // spatialToTrack +void QuadGraph::spatialToTrackUnrolled(Vec3 *dst, const Vec3& xyz, + const int parent_sector, const int unroll_qd_idx) const +{ + if (parent_sector == UNKNOWN_SECTOR) + { + Log::warn("Quad Graph", "UNKNOWN_SECTOR in spatialToTrack()."); + return; + } + getNode(parent_sector).getDistancesUnrolled(xyz, unroll_qd_idx, dst); +} + //----------------------------------------------------------------------------- /** findRoadSector returns in which sector on the road the position * xyz is. If xyz is not on top of the road, it sets UNKNOWN_SECTOR as sector. diff --git a/src/tracks/quad_graph.hpp b/src/tracks/quad_graph.hpp index ce15199df..f580307ec 100644 --- a/src/tracks/quad_graph.hpp +++ b/src/tracks/quad_graph.hpp @@ -110,6 +110,11 @@ public: bool for_ai=false) const; void spatialToTrack(Vec3 *dst, const Vec3& xyz, const int sector) const; + void spatialToTrackUnrolled(Vec3 *dst, + const Vec3& xyz, + const int parent_sector, + const int unroll_qd_idx) const; + void findRoadSector(const Vec3& XYZ, int *sector, std::vector *all_sectors=NULL) const; int findOutOfRoadSector(const Vec3& xyz, @@ -199,7 +204,7 @@ public: { return getNode(node).getUnrolledQuad(quad_number); } /** Returns the number of forward quads that are unrolled for each quad **/ - int getNumberOfUnrolledQuads() const { return m_unroll_count; } + int getNumberOfUnrolledQuads() const { return m_unroll_quad_count; } }; // QuadGraph From a06ec9407a41324b2aa23e90386a2f978869a087 Mon Sep 17 00:00:00 2001 From: nixt Date: Wed, 18 Jun 2014 08:45:16 +0530 Subject: [PATCH 033/350] New function GraphNode::getDistanceUnrolled to find distances from the unrolled quads --- src/tracks/graph_node.cpp | 14 ++++++++++++++ src/tracks/graph_node.hpp | 1 + 2 files changed, 15 insertions(+) diff --git a/src/tracks/graph_node.cpp b/src/tracks/graph_node.cpp index bac26bb2b..b6a397fb1 100644 --- a/src/tracks/graph_node.cpp +++ b/src/tracks/graph_node.cpp @@ -197,6 +197,20 @@ void GraphNode::getDistances(const Vec3 &xyz, Vec3 *result) (closest-m_lower_center_2d).getLength()); } // getDistances +void GraphNode::getDistancesUnrolled(const Vec3 &xyz, unsigned int quad_idx, Vec3 *result) +{ + + const Quad& unrolled = getUnrolledQuad(quad_idx); + Vec3 upper_center = 0.5f*(unrolled[2] + unrolled[3]), + lower_center = 0.5f*(unrolled[0] + unrolled[1]); + // center_line is from A to B, or lower_center to upper_center + core::vector3df A = lower_center.toIrrVector(), B = upper_center.toIrrVector(); + result->setX(((B - A).crossProduct(xyz.toIrrVector() - A)).getLength() / (B - A).getLength()); + + result->setZ(QuadGraph::get()->getNode(m_node_index + quad_idx).getDistanceFromStart() + + (xyz - lower_center).length()); +} + // ---------------------------------------------------------------------------- /** Returns the square of the distance between the given point and any point * on the 'centre' line, i.e. the finite line from the middle point of the diff --git a/src/tracks/graph_node.hpp b/src/tracks/graph_node.hpp index 4d8875ab7..a9e9fb417 100644 --- a/src/tracks/graph_node.hpp +++ b/src/tracks/graph_node.hpp @@ -135,6 +135,7 @@ public: GraphNode(unsigned int quad_index, unsigned int node_index); void addSuccessor (unsigned int to); void getDistances(const Vec3 &xyz, Vec3 *result); + void getDistancesUnrolled(const Vec3 &xyz, unsigned int quad_idx, Vec3 *result); float getDistance2FromPoint(const Vec3 &xyz); void setupPathsToNode(); void setChecklineRequirements(int latest_checkline); From 2249a73c50fa5de585696b92292d5a95bc7c80a4 Mon Sep 17 00:00:00 2001 From: nixt Date: Thu, 19 Jun 2014 18:07:52 +0530 Subject: [PATCH 034/350] Fix flattened quads. rotateVect was reading and writing to the same location earlier. --- src/tracks/quad.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tracks/quad.cpp b/src/tracks/quad.cpp index 90abfd5e6..fd9bea286 100644 --- a/src/tracks/quad.cpp +++ b/src/tracks/quad.cpp @@ -194,10 +194,10 @@ Quad Quad::getFlattenedQuad() for (unsigned int i = 0; i < 4; i++) { // Translate to origin - m_p_flat[i] = m_p[i] - m_center; + //m_p_flat[i] = m_p[i] - m_center; // rotateVect(out,in) - m.rotateVect(m_p_flat[i] , m_p_flat[i].toIrrVector()); + m.rotateVect(m_p_flat[i], (m_p[i] - m_center).toIrrVector()); m_p_flat[i].setY(0); } From 0c3cded260ef896a6da80ec184ad0909200a5049 Mon Sep 17 00:00:00 2001 From: nixt Date: Thu, 19 Jun 2014 18:11:18 +0530 Subject: [PATCH 035/350] get distances would crash by accessing m_node_index + quad_idx . This quad does not exist when m_node_index is the last quad. --- src/tracks/graph_node.cpp | 16 ++++++++-------- src/tracks/quad_graph.cpp | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/tracks/graph_node.cpp b/src/tracks/graph_node.cpp index b6a397fb1..50310accb 100644 --- a/src/tracks/graph_node.cpp +++ b/src/tracks/graph_node.cpp @@ -207,7 +207,7 @@ void GraphNode::getDistancesUnrolled(const Vec3 &xyz, unsigned int quad_idx, Vec core::vector3df A = lower_center.toIrrVector(), B = upper_center.toIrrVector(); result->setX(((B - A).crossProduct(xyz.toIrrVector() - A)).getLength() / (B - A).getLength()); - result->setZ(QuadGraph::get()->getNode(m_node_index + quad_idx).getDistanceFromStart() + result->setZ(QuadGraph::get()->getNode((m_node_index + quad_idx)%QuadGraph::get()->getNumNodes()).getDistanceFromStart() + (xyz - lower_center).length()); } @@ -284,13 +284,13 @@ void GraphNode::addUnrolledQuad(const GraphNode& next_node, int k) for (unsigned int i = 0; i < 4; i++) m.rotateVect(new_points[i], next_quad_to_push[i].toIrrVector()); - Vec3 endEdge = 0.5f*(last_pushed_quad[2] + last_pushed_quad[1]) - 0.5f*(last_pushed_quad[3] + last_pushed_quad[0]); - Vec3 beginEdge = 0.5f*(new_points[1] + new_points[2]) - 0.5f*(new_points[0] + new_points[3]); - /* - m.buildRotateFromTo(beginEdge.toIrrVector(), endEdge.toIrrVector()); - for (unsigned int i = 0; i < 4; i++) - m.rotateVect(new_points[i]); - */ + Vec3 endEdge = (last_pushed_quad[2] - last_pushed_quad[3]); + Vec3 beginEdge = (new_points[1] - new_points[0]); + + //m.buildRotateFromTo(beginEdge.toIrrVector(), endEdge.toIrrVector()); + //for (unsigned int i = 0; i < 4; i++) + // m.rotateVect(new_points[i]); + // Next translate the new quad to be pushed to the correct position infront // of the last quad in the vector of unrolled quads Vec3 lower_center = 0.5f*(new_points[0] + new_points[1]); diff --git a/src/tracks/quad_graph.cpp b/src/tracks/quad_graph.cpp index 38a26e931..89b7baae5 100644 --- a/src/tracks/quad_graph.cpp +++ b/src/tracks/quad_graph.cpp @@ -54,7 +54,7 @@ QuadGraph::QuadGraph(const std::string &quad_file_name, m_mesh = NULL; m_mesh_buffer = NULL; m_lap_length = 0; - m_unroll_quad_count = 10; + m_unroll_quad_count = 7; QuadSet::create(); QuadSet::get()->init(quad_file_name); m_quad_filename = quad_file_name; @@ -576,7 +576,7 @@ void QuadGraph::createMesh2() c.setGreen((3 * i) % 256); } - Quad flatquad = QuadGraph::get()->getNode(28).getUnrolledQuad(count); + Quad flatquad = QuadGraph::get()->getNode(57).getUnrolledQuad(count); //std::vector vInd = poly.getVerticesIndex(); // Four vertices for each of the n-1 remaining quads From 652af9a0a1cf242c971644b056f744ccbe528910 Mon Sep 17 00:00:00 2001 From: nixt Date: Thu, 19 Jun 2014 18:12:10 +0530 Subject: [PATCH 036/350] Adapt findNonCrashingPoint to work in 3d --- src/karts/controller/skidding_ai.cpp | 48 ++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index 3b8996cdf..ceb8094c3 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -35,7 +35,7 @@ #endif #include "karts/controller/skidding_ai.hpp" - +#define AI_DEBUG #ifdef AI_DEBUG # include "graphics/irr_driver.hpp" #endif @@ -2009,7 +2009,8 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) float angle = QuadGraph::get()->getAngleToNext(m_track_node, m_successor_index[m_track_node]); int target_sector; - + Vec3 transfrmd_kart_coord = QuadGraph::get()->getNode(m_track_node). + getPointTransformedToFlatQuad(m_kart->getXYZ()); Vec3 direction; Vec3 step_track_coord; @@ -2017,11 +2018,15 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) // The original while(1) loop is replaced with a for loop to avoid // infinite loops (which we had once or twice). Usually the number // of iterations in the while loop is less than 7. - for(unsigned int j=0; j<100; j++) + for(unsigned int j=0; jgetNumberOfUnrolledQuads(); j++) { // target_sector is the sector at the longest distance that we can // drive to without crashing with the track. target_sector = m_next_node_index[*last_node]; + + + /** Skip angle checking for now + angle1 = QuadGraph::get()->getAngleToNext(target_sector, m_successor_index[target_sector]); // In very sharp turns this algorithm tends to aim at off track points, @@ -2034,12 +2039,18 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) .getCenter(); return; } + */ //direction is a vector from our kart to the sectors we are testing - direction = QuadGraph::get()->getQuadOfNode(target_sector).getCenter() - - m_kart->getXYZ(); + //direction = QuadGraph::get()->getQuadOfNode(target_sector).getCenter() + // - m_kart->getXYZ(); - float len=direction.length_2d(); + Quad target_quad_unrolled = QuadGraph::get()->getNode(m_track_node). + getUnrolledQuad(j + 1); + direction = target_quad_unrolled.getCenter() - m_kart->getXYZ(); + + //float len=direction.length_2d(); + float len = direction.length(); unsigned int steps = (unsigned int)( len / m_kart_length ); if( steps < 3 ) steps = 3; @@ -2058,25 +2069,34 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) for(unsigned int i = 2; i < steps; ++i ) { step_coord = m_kart->getXYZ()+direction*m_kart_length * float(i); + //step_coord = transfrmd_kart_coord+direction*m_kart_length * float(i); + + //QuadGraph::get()->spatialToTrack(&step_track_coord, step_coord, + // *last_node ); + QuadGraph::get()->spatialToTrackUnrolled(&step_track_coord, step_coord, + *last_node, j); - QuadGraph::get()->spatialToTrack(&step_track_coord, step_coord, - *last_node ); - float distance = fabsf(step_track_coord[0]); + //float distance = fabsf(step_track_coord[0]); + float distance = step_track_coord[0]; //If we are outside, the previous node is what we are looking for if ( distance + m_kart_width * 0.5f - > QuadGraph::get()->getNode(*last_node).getPathWidth() ) + > QuadGraph::get()->getNode((*last_node+j)%QuadGraph::get()->getNumNodes() ).getPathWidth() ) { - *aim_position = QuadGraph::get()->getQuadOfNode(*last_node) + *aim_position = QuadGraph::get()->getUnrolledQuadOfNode(*last_node,j) .getCenter(); return; } + + } - angle = angle1; - *last_node = target_sector; + // angle = angle1; + //*last_node = target_sector; } // for i<100 - *aim_position = QuadGraph::get()->getQuadOfNode(*last_node).getCenter(); + *aim_position = QuadGraph::get()->getNode(m_track_node). + getUnrolledQuad(QuadGraph::get()->getNumberOfUnrolledQuads()).getCenter(); + //*aim_position = QuadGraph::get()->getQuadOfNode(*last_node).getCenter(); } // findNonCrashingPoint //----------------------------------------------------------------------------- From fc7692819a1c1b1ed625b2f1176a2a24206054f8 Mon Sep 17 00:00:00 2001 From: nixt Date: Thu, 19 Jun 2014 18:12:25 +0530 Subject: [PATCH 037/350] Adapt steerToPoint to work in 3D --- src/karts/controller/ai_base_controller.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/karts/controller/ai_base_controller.cpp b/src/karts/controller/ai_base_controller.cpp index a192eb5ba..e8eb0015a 100644 --- a/src/karts/controller/ai_base_controller.cpp +++ b/src/karts/controller/ai_base_controller.cpp @@ -359,6 +359,9 @@ float AIBaseController::steerToPoint(const Vec3 &point) btQuaternion q(btVector3(0,1,0), -m_kart->getHeading()); Vec3 p = point - m_kart->getXYZ(); Vec3 lc = quatRotate(q, p); + + btTransform trans = m_kart->getTrans().inverse(); + lc = trans(point); // The point the kart is aiming at can be reached 'incorrectly' if the // point is below the y=x line: Instead of aiming at that point directly From 70ad2d58db6dc066ccaa88726ec8f2b6440c0b47 Mon Sep 17 00:00:00 2001 From: nixt Date: Sun, 22 Jun 2014 11:43:54 +0530 Subject: [PATCH 038/350] Remove test for vertical distance of kart from sector. --- src/tracks/quad_graph.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/tracks/quad_graph.cpp b/src/tracks/quad_graph.cpp index 89b7baae5..75f6c4a3b 100644 --- a/src/tracks/quad_graph.cpp +++ b/src/tracks/quad_graph.cpp @@ -54,7 +54,7 @@ QuadGraph::QuadGraph(const std::string &quad_file_name, m_mesh = NULL; m_mesh_buffer = NULL; m_lap_length = 0; - m_unroll_quad_count = 7; + m_unroll_quad_count = 6; QuadSet::create(); QuadSet::get()->init(quad_file_name); m_quad_filename = quad_file_name; @@ -576,7 +576,7 @@ void QuadGraph::createMesh2() c.setGreen((3 * i) % 256); } - Quad flatquad = QuadGraph::get()->getNode(57).getUnrolledQuad(count); + Quad flatquad = QuadGraph::get()->getNode(59).getUnrolledQuad(count); //std::vector vInd = poly.getVerticesIndex(); // Four vertices for each of the n-1 remaining quads @@ -938,7 +938,9 @@ void QuadGraph::findRoadSector(const Vec3& xyz, int *sector, // Now we search through all graph nodes, starting with // the current one int indx = *sector; - float min_dist = 999999.9f; + // This was used to check the vertical distance of kart from sector + // but because now karts are checked in a 3D space, this is not required + //float min_dist = 999999.9f; // If a current sector is given, and max_lookahead is specify, only test // the next max_lookahead graph nodes instead of testing the whole graph. @@ -963,9 +965,9 @@ void QuadGraph::findRoadSector(const Vec3& xyz, int *sector, float dist = xyz.getY() - q.getMinHeight(); // While negative distances are unlikely, we allow some small negative // numbers in case that the kart is partly in the track. - if(q.pointInQuad3D(xyz) && dist < min_dist && dist>-1.0f) + if(q.pointInQuad3D(xyz))// && dist < min_dist && dist>-1.0f) { - min_dist = dist; + //min_dist = dist; *sector = indx; } } // for i Date: Sun, 22 Jun 2014 11:49:29 +0530 Subject: [PATCH 039/350] Adjust angle to next computation to work with 3D quads. --- src/tracks/graph_node.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/tracks/graph_node.cpp b/src/tracks/graph_node.cpp index 50310accb..ee8cc676c 100644 --- a/src/tracks/graph_node.cpp +++ b/src/tracks/graph_node.cpp @@ -92,9 +92,16 @@ void GraphNode::addSuccessor(unsigned int to) 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())); + + core::CMatrix4 m; + m.buildRotateFromTo(this_quad.getNormal().toIrrVector(), + Vec3(0, 1, 0).toIrrVector()); + core::vector3df diffRotated; + m.rotateVect(diffRotated, diff.toIrrVector()); + + m_angle_to_next.push_back(atan2(diffRotated.X, diffRotated.Z)); } // addSuccessor @@ -287,9 +294,9 @@ void GraphNode::addUnrolledQuad(const GraphNode& next_node, int k) Vec3 endEdge = (last_pushed_quad[2] - last_pushed_quad[3]); Vec3 beginEdge = (new_points[1] - new_points[0]); - //m.buildRotateFromTo(beginEdge.toIrrVector(), endEdge.toIrrVector()); - //for (unsigned int i = 0; i < 4; i++) - // m.rotateVect(new_points[i]); + m.buildRotateFromTo(beginEdge.toIrrVector(), endEdge.toIrrVector()); + for (unsigned int i = 0; i < 4; i++) + m.rotateVect(new_points[i]); // Next translate the new quad to be pushed to the correct position infront // of the last quad in the vector of unrolled quads From be43273f80243aab1429793c269f641ae48dc733 Mon Sep 17 00:00:00 2001 From: nixt Date: Sun, 22 Jun 2014 11:51:58 +0530 Subject: [PATCH 040/350] Change how angle_to_track is determined. Now it uses the kart velocity instead of the heading. --- src/karts/controller/skidding_ai.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index ceb8094c3..92ce588e9 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -2025,7 +2025,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) target_sector = m_next_node_index[*last_node]; - /** Skip angle checking for now + angle1 = QuadGraph::get()->getAngleToNext(target_sector, m_successor_index[target_sector]); @@ -2039,7 +2039,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) .getCenter(); return; } - */ + //direction is a vector from our kart to the sectors we are testing //direction = QuadGraph::get()->getQuadOfNode(target_sector).getCenter() @@ -2107,8 +2107,18 @@ void SkiddingAI::determineTrackDirection() { const QuadGraph *qg = QuadGraph::get(); unsigned int succ = m_successor_index[m_track_node]; - float angle_to_track = qg->getNode(m_track_node).getAngleToSuccessor(succ) - - m_kart->getHeading(); + unsigned int next = qg->getNode(m_track_node).getSuccessor(succ); + + //float angle_to_track = qg->getNode(m_track_node).getAngleToSuccessor(succ) + // - m_kart->getHeading(); + Vec3 track_direction = -qg->getQuadOfNode(m_track_node).getCenter() + + qg->getQuadOfNode(next).getCenter(); + //Vec3 kart_direction = qg->getQuadOfNode(m_track_node).getCenter() + m_kart->getVelocity(); + + float angle_to_track = 0; + if (m_kart->getVelocity().length() > 0.0f) + angle_to_track = track_direction.angle(m_kart->getVelocity().normalized()); + angle_to_track = normalizeAngle(angle_to_track); // In certain circumstances (esp. S curves) it is possible that the @@ -2128,7 +2138,7 @@ void SkiddingAI::determineTrackDirection() return; } - unsigned int next = qg->getNode(m_track_node).getSuccessor(succ); + qg->getNode(next).getDirectionData(m_successor_index[next], &m_current_track_direction, From c1dd87d95bfea694c78567684b7f1d77a6082d61 Mon Sep 17 00:00:00 2001 From: nixt Date: Sun, 22 Jun 2014 13:07:38 +0530 Subject: [PATCH 041/350] Fix copy-paste error in pointInQuad function. --- src/tracks/quad.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tracks/quad.cpp b/src/tracks/quad.cpp index fd9bea286..03351c0cb 100644 --- a/src/tracks/quad.cpp +++ b/src/tracks/quad.cpp @@ -124,7 +124,7 @@ bool Quad::pointInQuad3D(const Vec3& p) const // All this code for computing the quad bounding box should be later be // moved to somewhere in the constructor and computed only once per quad. Vec3 boxCorners[8]; - Vec3 normal = (m_p[1] - m_p[0]).cross(m_p[2] - m_p[1]); + Vec3 normal = -(m_p[1] - m_p[0]).cross(m_p[2] - m_p[1]); normal.normalize(); float boxHigh = 5.0f; float boxLow = 1.0f; @@ -132,10 +132,10 @@ bool Quad::pointInQuad3D(const Vec3& p) const boxCorners[1] = m_p[1] + boxHigh*normal; boxCorners[2] = m_p[2] + boxHigh*normal; boxCorners[3] = m_p[3] + boxHigh*normal; - boxCorners[4] = m_p[0] - boxLow*m_p[1]; - boxCorners[5] = m_p[1] - boxLow*m_p[1]; - boxCorners[6] = m_p[2] - boxLow*m_p[1]; - boxCorners[7] = m_p[3] - boxLow*m_p[1]; + boxCorners[4] = m_p[0] - boxLow*normal; + boxCorners[5] = m_p[1] - boxLow*normal; + boxCorners[6] = m_p[2] - boxLow*normal; + boxCorners[7] = m_p[3] - boxLow*normal; const Vec3 boxFaces[6][4] = { { boxCorners[0], boxCorners[1], boxCorners[2], boxCorners[3] }, From 3a7fc1e0f9a5f24b91901a01268b6a808c31e67f Mon Sep 17 00:00:00 2001 From: nixt Date: Sun, 22 Jun 2014 13:09:05 +0530 Subject: [PATCH 042/350] The AI can now finally drive a loop in newton. --- src/tracks/graph_node.cpp | 2 +- src/tracks/quad_graph.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tracks/graph_node.cpp b/src/tracks/graph_node.cpp index ee8cc676c..7bc736ca1 100644 --- a/src/tracks/graph_node.cpp +++ b/src/tracks/graph_node.cpp @@ -273,7 +273,7 @@ void GraphNode::addUnrolledQuad(const GraphNode& next_node, int k) if (k == 0) return; Quad next_quad = next_node.getQuad(); - Quad next_quad_to_push = next_quad; + Quad next_quad_to_push = next_quad.getFlattenedQuad(); Quad last_pushed_quad = m_unrolled_quads.back(); diff --git a/src/tracks/quad_graph.cpp b/src/tracks/quad_graph.cpp index 75f6c4a3b..951a743c3 100644 --- a/src/tracks/quad_graph.cpp +++ b/src/tracks/quad_graph.cpp @@ -576,7 +576,7 @@ void QuadGraph::createMesh2() c.setGreen((3 * i) % 256); } - Quad flatquad = QuadGraph::get()->getNode(59).getUnrolledQuad(count); + Quad flatquad = QuadGraph::get()->getNode(55).getUnrolledQuad(count); //std::vector vInd = poly.getVerticesIndex(); // Four vertices for each of the n-1 remaining quads From c489f5ca19e6ddd8e4f9a4de53fc981069916e88 Mon Sep 17 00:00:00 2001 From: nixt Date: Sun, 22 Jun 2014 17:16:23 +0530 Subject: [PATCH 043/350] Fix compilation problem in linux --- src/tracks/graph_node.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tracks/graph_node.hpp b/src/tracks/graph_node.hpp index a9e9fb417..748d02865 100644 --- a/src/tracks/graph_node.hpp +++ b/src/tracks/graph_node.hpp @@ -129,7 +129,7 @@ private: void markAllSuccessorsToUse(unsigned int n, PathToNodeVector *m_path_to_node); - void GraphNode::addUnrolledQuad(const GraphNode& next_node,int k); + void addUnrolledQuad(const GraphNode& next_node,int k); public: GraphNode(unsigned int quad_index, unsigned int node_index); From 0a66a6f7dd735f9518db74f33ad96e9999be5380 Mon Sep 17 00:00:00 2001 From: nixt Date: Sun, 22 Jun 2014 18:05:16 +0530 Subject: [PATCH 044/350] Workaround to enable visual aids for track debugging. --- src/graphics/irr_driver.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 6620ed7df..28b76b4f7 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -447,6 +447,9 @@ void IrrDriver::initDevice() Log::info("IrrDriver", "OpenGL vendor: %s", glGetString(GL_VENDOR)); Log::info("IrrDriver", "OpenGL renderer: %s", glGetString(GL_RENDERER)); Log::info("IrrDriver", "OpenGL version string: %s", glGetString(GL_VERSION)); + //m_glsl = (GLMajorVersion > 3 || (GLMajorVersion == 3 && GLMinorVersion >= 1)); + m_glsl = false; + // Parse extensions hasVSLayer = false; // Default false value for hasVSLayer if --no-graphics argument is used From e0d2777c7d219a34e6dc952ee7289083072f4ca8 Mon Sep 17 00:00:00 2001 From: nixt Date: Mon, 23 Jun 2014 12:47:24 +0530 Subject: [PATCH 045/350] [Optimization] Moved code for computing quad bounding boxes to constructor. --- src/tracks/quad.cpp | 48 ++++++++++++++++++++++----------------------- src/tracks/quad.hpp | 4 ++++ 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/tracks/quad.cpp b/src/tracks/quad.cpp index 03351c0cb..f52adfbe7 100644 --- a/src/tracks/quad.cpp +++ b/src/tracks/quad.cpp @@ -54,6 +54,29 @@ Quad::Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, m_ai_ignore = ai_ignore; findNormal(); + + // Compute the quad bounding box used for pointInQuad + Vec3 boxCorners[8]; + Vec3 normal = getNormal(); + float boxHigh = 5.0f; + float boxLow = 1.0f; + boxCorners[0] = m_p[0] + boxHigh*normal; + boxCorners[1] = m_p[1] + boxHigh*normal; + boxCorners[2] = m_p[2] + boxHigh*normal; + boxCorners[3] = m_p[3] + boxHigh*normal; + boxCorners[4] = m_p[0] - boxLow*normal; + boxCorners[5] = m_p[1] - boxLow*normal; + boxCorners[6] = m_p[2] - boxLow*normal; + boxCorners[7] = m_p[3] - boxLow*normal; + + boxFaces = {{ + {{ boxCorners[0], boxCorners[1], boxCorners[2], boxCorners[3] }}, + {{ boxCorners[3], boxCorners[2], boxCorners[6], boxCorners[7] }}, + {{ boxCorners[7], boxCorners[6], boxCorners[5], boxCorners[4] }}, + {{ boxCorners[1], boxCorners[0], boxCorners[4], boxCorners[5] }}, + {{ boxCorners[4], boxCorners[0], boxCorners[3], boxCorners[7] }}, + {{ boxCorners[1], boxCorners[5], boxCorners[6], boxCorners[2] }} + }}; } // Quad @@ -121,37 +144,12 @@ bool Quad::pointInQuad(const Vec3& p) const bool Quad::pointInQuad3D(const Vec3& p) const { - // All this code for computing the quad bounding box should be later be - // moved to somewhere in the constructor and computed only once per quad. - Vec3 boxCorners[8]; - Vec3 normal = -(m_p[1] - m_p[0]).cross(m_p[2] - m_p[1]); - normal.normalize(); - float boxHigh = 5.0f; - float boxLow = 1.0f; - boxCorners[0] = m_p[0] + boxHigh*normal; - boxCorners[1] = m_p[1] + boxHigh*normal; - boxCorners[2] = m_p[2] + boxHigh*normal; - boxCorners[3] = m_p[3] + boxHigh*normal; - boxCorners[4] = m_p[0] - boxLow*normal; - boxCorners[5] = m_p[1] - boxLow*normal; - boxCorners[6] = m_p[2] - boxLow*normal; - boxCorners[7] = m_p[3] - boxLow*normal; - - const Vec3 boxFaces[6][4] = { - { boxCorners[0], boxCorners[1], boxCorners[2], boxCorners[3] }, - { boxCorners[3], boxCorners[2], boxCorners[6], boxCorners[7] }, - { boxCorners[7], boxCorners[6], boxCorners[5], boxCorners[4] }, - { boxCorners[1], boxCorners[0], boxCorners[4], boxCorners[5] }, - { boxCorners[4], boxCorners[0], boxCorners[3], boxCorners[7] }, - { boxCorners[1], boxCorners[5], boxCorners[6], boxCorners[2] } }; - float side = p.sideofPlane(boxFaces[0][0], boxFaces[0][1], boxFaces[0][2]); for (int i = 1; i < 6; i++) { if (side*p.sideofPlane(boxFaces[i][0], boxFaces[i][1], boxFaces[i][2]) < 0) return false; } return true; - } diff --git a/src/tracks/quad.hpp b/src/tracks/quad.hpp index 914480578..80ded20db 100644 --- a/src/tracks/quad.hpp +++ b/src/tracks/quad.hpp @@ -22,6 +22,8 @@ #include #include "utils/vec3.hpp" +#include + namespace irr { namespace video { struct S3DVertex; } @@ -59,6 +61,8 @@ private: /** Set if this quad should not be used by the AI. */ bool m_ai_ignore; + std::array , 6 > boxFaces; + void findNormal(); public: From 450d8d1ed22cc2cf198ac8c697ab5fdc603fd580 Mon Sep 17 00:00:00 2001 From: nixt Date: Sat, 28 Jun 2014 16:16:24 +0530 Subject: [PATCH 046/350] Enable AI to take alternate paths. More testing needed. --- src/karts/controller/skidding_ai.cpp | 41 +++++++++++++++----------- src/tracks/graph_node.cpp | 43 +++++++++++++++++++++------- src/tracks/graph_node.hpp | 9 +++--- src/tracks/quad_graph.cpp | 6 ++-- src/tracks/quad_graph.hpp | 7 +++-- 5 files changed, 68 insertions(+), 38 deletions(-) diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index 92ce588e9..62bf3ad3f 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -238,7 +238,9 @@ void SkiddingAI::update(float dt) // This is used to enable firing an item backwards. m_controls->m_look_back = false; m_controls->m_nitro = false; - //Log::info("Sector", "%d", m_track_node); + + //Vec3 gravity = m_kart->getBody()->getGravity(); + //Log::info("Sector", "%f %f %f %d", gravity[0], gravity[1], gravity[2],m_track_node); // Don't do anything if there is currently a kart animations shown. if(m_kart->getKartAnimation()) @@ -2009,12 +2011,22 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) float angle = QuadGraph::get()->getAngleToNext(m_track_node, m_successor_index[m_track_node]); int target_sector; - Vec3 transfrmd_kart_coord = QuadGraph::get()->getNode(m_track_node). - getPointTransformedToFlatQuad(m_kart->getXYZ()); + Vec3 direction; Vec3 step_track_coord; float angle1; + + int future_successor_idx = 0; + int current = m_track_node; + for (unsigned int j = 0; j < QuadGraph::get()->getNumberOfUnrolledQuads(); j++) + { + if (m_successor_index[current] != 0) + future_successor_idx = m_successor_index[m_track_node]; + + current = m_next_node_index[current]; + } + // The original while(1) loop is replaced with a for loop to avoid // infinite loops (which we had once or twice). Usually the number // of iterations in the while loop is less than 7. @@ -2023,9 +2035,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) // target_sector is the sector at the longest distance that we can // drive to without crashing with the track. target_sector = m_next_node_index[*last_node]; - - - + angle1 = QuadGraph::get()->getAngleToNext(target_sector, m_successor_index[target_sector]); @@ -2041,15 +2051,13 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) } - //direction is a vector from our kart to the sectors we are testing - //direction = QuadGraph::get()->getQuadOfNode(target_sector).getCenter() - // - m_kart->getXYZ(); - + Quad target_quad_unrolled = QuadGraph::get()->getNode(m_track_node). - getUnrolledQuad(j + 1); + getUnrolledQuad(future_successor_idx,j + 1); + direction = target_quad_unrolled.getCenter() - m_kart->getXYZ(); - //float len=direction.length_2d(); + float len = direction.length(); unsigned int steps = (unsigned int)( len / m_kart_length ); if( steps < 3 ) steps = 3; @@ -2069,12 +2077,11 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) for(unsigned int i = 2; i < steps; ++i ) { step_coord = m_kart->getXYZ()+direction*m_kart_length * float(i); - //step_coord = transfrmd_kart_coord+direction*m_kart_length * float(i); - + //QuadGraph::get()->spatialToTrack(&step_track_coord, step_coord, // *last_node ); QuadGraph::get()->spatialToTrackUnrolled(&step_track_coord, step_coord, - *last_node, j); + m_track_node, j, future_successor_idx); //float distance = fabsf(step_track_coord[0]); @@ -2084,7 +2091,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) if ( distance + m_kart_width * 0.5f > QuadGraph::get()->getNode((*last_node+j)%QuadGraph::get()->getNumNodes() ).getPathWidth() ) { - *aim_position = QuadGraph::get()->getUnrolledQuadOfNode(*last_node,j) + *aim_position = QuadGraph::get()->getUnrolledQuadOfNode(m_track_node,future_successor_idx,j) .getCenter(); return; } @@ -2095,7 +2102,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) //*last_node = target_sector; } // for i<100 *aim_position = QuadGraph::get()->getNode(m_track_node). - getUnrolledQuad(QuadGraph::get()->getNumberOfUnrolledQuads()).getCenter(); + getUnrolledQuad(future_successor_idx,QuadGraph::get()->getNumberOfUnrolledQuads()).getCenter(); //*aim_position = QuadGraph::get()->getQuadOfNode(*last_node).getCenter(); } // findNonCrashingPoint diff --git a/src/tracks/graph_node.cpp b/src/tracks/graph_node.cpp index 7bc736ca1..08d63b577 100644 --- a/src/tracks/graph_node.cpp +++ b/src/tracks/graph_node.cpp @@ -204,10 +204,10 @@ void GraphNode::getDistances(const Vec3 &xyz, Vec3 *result) (closest-m_lower_center_2d).getLength()); } // getDistances -void GraphNode::getDistancesUnrolled(const Vec3 &xyz, unsigned int quad_idx, Vec3 *result) +void GraphNode::getDistancesUnrolled(const Vec3 &xyz, const int fork_number, unsigned int quad_idx, Vec3 *result) { - const Quad& unrolled = getUnrolledQuad(quad_idx); + const Quad& unrolled = getUnrolledQuad(fork_number,quad_idx); Vec3 upper_center = 0.5f*(unrolled[2] + unrolled[3]), lower_center = 0.5f*(unrolled[0] + unrolled[1]); // center_line is from A to B, or lower_center to upper_center @@ -258,23 +258,43 @@ void GraphNode::buildUnrolledQuads(unsigned int unroll_quad_count) { m_unrolled_quads.clear(); - Quad thisQuad = getQuad(); + + unsigned int numberOfForks = 1; + GraphNode* currentNode = this; + for (int i = 0; i < unroll_quad_count+1; i++) + { + unsigned int successorCount = currentNode->getNumberOfSuccessors(); + if (successorCount >= 2) + { + numberOfForks = successorCount; + break; + } + else + { + currentNode = &QuadGraph::get()->getNode(currentNode->getSuccessor(0)); + + } + } - m_unrolled_quads.push_back(thisQuad); - - //unsigned int successorCount = getNumberOfSuccessors(); - addUnrolledQuad(QuadGraph::get()->getNode(getSuccessor(0)), unroll_quad_count); + m_unrolled_quads.resize(numberOfForks); + for (int i = 0; i < numberOfForks; i++) + { + Quad thisQuad = getQuad(); + m_unrolled_quads[i].push_back(thisQuad); + GraphNode& next = QuadGraph::get()->getNode(getSuccessor(i%getNumberOfSuccessors())); + addUnrolledQuad(next , i , unroll_quad_count); + } } -void GraphNode::addUnrolledQuad(const GraphNode& next_node, int k) +void GraphNode::addUnrolledQuad(const GraphNode& next_node, int fork_number, int k) { if (k == 0) return; Quad next_quad = next_node.getQuad(); Quad next_quad_to_push = next_quad.getFlattenedQuad(); - Quad last_pushed_quad = m_unrolled_quads.back(); + Quad last_pushed_quad = m_unrolled_quads[fork_number].back(); core::CMatrix4 m; @@ -308,8 +328,9 @@ void GraphNode::addUnrolledQuad(const GraphNode& next_node, int k) m.translateVect(new_points[i]); // Push the quad into the vector of unrolled quads - m_unrolled_quads.push_back(Quad(new_points[0], new_points[1], new_points[2], new_points[3])); + m_unrolled_quads[fork_number].push_back(Quad(new_points[0], new_points[1], new_points[2], new_points[3])); k = k - 1; // Recurisvely build the vector of unrolled quads till k reduces to 0 - addUnrolledQuad(QuadGraph::get()->getNode(next_node.getSuccessor(0)), k); + GraphNode& next = QuadGraph::get()->getNode(next_node.getSuccessor(fork_number%next_node.getNumberOfSuccessors())); + addUnrolledQuad(next, fork_number, k); } \ No newline at end of file diff --git a/src/tracks/graph_node.hpp b/src/tracks/graph_node.hpp index 748d02865..af1f109a1 100644 --- a/src/tracks/graph_node.hpp +++ b/src/tracks/graph_node.hpp @@ -124,18 +124,19 @@ private: */ std::vector< int > m_checkline_requirements; - std::vector m_unrolled_quads; + std::vector< std::vector >m_unrolled_quads; void markAllSuccessorsToUse(unsigned int n, PathToNodeVector *m_path_to_node); - void addUnrolledQuad(const GraphNode& next_node,int k); + void addUnrolledQuad(const GraphNode& next_node, int fork_number, int k); public: GraphNode(unsigned int quad_index, unsigned int node_index); void addSuccessor (unsigned int to); void getDistances(const Vec3 &xyz, Vec3 *result); - void getDistancesUnrolled(const Vec3 &xyz, unsigned int quad_idx, Vec3 *result); + void getDistancesUnrolled(const Vec3 &xyz, const int fork_number, + unsigned int quad_idx, Vec3 *result); float getDistance2FromPoint(const Vec3 &xyz); void setupPathsToNode(); void setChecklineRequirements(int latest_checkline); @@ -241,7 +242,7 @@ public: const Vec3 getPointTransformedToFlatQuad(Vec3 xyz); - const Quad& getUnrolledQuad(int i) const { return m_unrolled_quads[i]; } + const Quad& getUnrolledQuad(int succ_idx, int i) const { return m_unrolled_quads[succ_idx][i]; } }; // GraphNode #endif diff --git a/src/tracks/quad_graph.cpp b/src/tracks/quad_graph.cpp index 951a743c3..235d9ff9c 100644 --- a/src/tracks/quad_graph.cpp +++ b/src/tracks/quad_graph.cpp @@ -576,7 +576,7 @@ void QuadGraph::createMesh2() c.setGreen((3 * i) % 256); } - Quad flatquad = QuadGraph::get()->getNode(55).getUnrolledQuad(count); + Quad flatquad = QuadGraph::get()->getNode(55).getUnrolledQuad(0,count); //std::vector vInd = poly.getVerticesIndex(); // Four vertices for each of the n-1 remaining quads @@ -903,14 +903,14 @@ void QuadGraph::spatialToTrack(Vec3 *dst, const Vec3& xyz, } // spatialToTrack void QuadGraph::spatialToTrackUnrolled(Vec3 *dst, const Vec3& xyz, - const int parent_sector, const int unroll_qd_idx) const + const int parent_sector, const int unroll_qd_idx, const int fork_number) const { if (parent_sector == UNKNOWN_SECTOR) { Log::warn("Quad Graph", "UNKNOWN_SECTOR in spatialToTrack()."); return; } - getNode(parent_sector).getDistancesUnrolled(xyz, unroll_qd_idx, dst); + getNode(parent_sector).getDistancesUnrolled(xyz, fork_number, unroll_qd_idx, dst); } //----------------------------------------------------------------------------- diff --git a/src/tracks/quad_graph.hpp b/src/tracks/quad_graph.hpp index f580307ec..a2175c026 100644 --- a/src/tracks/quad_graph.hpp +++ b/src/tracks/quad_graph.hpp @@ -113,7 +113,8 @@ public: void spatialToTrackUnrolled(Vec3 *dst, const Vec3& xyz, const int parent_sector, - const int unroll_qd_idx) const; + const int unroll_qd_idx, + const int fork_number) const; void findRoadSector(const Vec3& XYZ, int *sector, std::vector *all_sectors=NULL) const; @@ -200,8 +201,8 @@ public: /** Returns true if the graph is to be reversed. */ bool isReverse() const {return m_reverse; } - const Quad& getUnrolledQuadOfNode(unsigned int node, unsigned int quad_number) - { return getNode(node).getUnrolledQuad(quad_number); } + const Quad& getUnrolledQuadOfNode(unsigned int node, unsigned int fork_number, unsigned int quad_number) + { return getNode(node).getUnrolledQuad(fork_number,quad_number); } /** Returns the number of forward quads that are unrolled for each quad **/ int getNumberOfUnrolledQuads() const { return m_unroll_quad_count; } From 1bf287d4ccf4cc50122418f58e02cf29f846b453 Mon Sep 17 00:00:00 2001 From: nixt Date: Sat, 28 Jun 2014 16:17:09 +0530 Subject: [PATCH 047/350] Avoid use of C++11 std::array. --- src/tracks/quad.cpp | 24 ++++++++++++++---------- src/tracks/quad.hpp | 2 +- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/tracks/quad.cpp b/src/tracks/quad.cpp index f52adfbe7..eff177e05 100644 --- a/src/tracks/quad.cpp +++ b/src/tracks/quad.cpp @@ -69,14 +69,18 @@ Quad::Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, boxCorners[6] = m_p[2] - boxLow*normal; boxCorners[7] = m_p[3] - boxLow*normal; - boxFaces = {{ - {{ boxCorners[0], boxCorners[1], boxCorners[2], boxCorners[3] }}, - {{ boxCorners[3], boxCorners[2], boxCorners[6], boxCorners[7] }}, - {{ boxCorners[7], boxCorners[6], boxCorners[5], boxCorners[4] }}, - {{ boxCorners[1], boxCorners[0], boxCorners[4], boxCorners[5] }}, - {{ boxCorners[4], boxCorners[0], boxCorners[3], boxCorners[7] }}, - {{ boxCorners[1], boxCorners[5], boxCorners[6], boxCorners[2] }} - }}; + Vec3 boxFaces[6][4] = { + { boxCorners[0], boxCorners[1], boxCorners[2], boxCorners[3] }, + { boxCorners[3], boxCorners[2], boxCorners[6], boxCorners[7] }, + { boxCorners[7], boxCorners[6], boxCorners[5], boxCorners[4] }, + { boxCorners[1], boxCorners[0], boxCorners[4], boxCorners[5] }, + { boxCorners[4], boxCorners[0], boxCorners[3], boxCorners[7] }, + { boxCorners[1], boxCorners[5], boxCorners[6], boxCorners[2] } + }; + + for (unsigned int i = 0; i < 6 ; i++) + for (unsigned int j = 0; j < 4; j++) + m_box_faces[i][j] = boxFaces[i][j]; } // Quad @@ -144,10 +148,10 @@ bool Quad::pointInQuad(const Vec3& p) const bool Quad::pointInQuad3D(const Vec3& p) const { - float side = p.sideofPlane(boxFaces[0][0], boxFaces[0][1], boxFaces[0][2]); + float side = p.sideofPlane(m_box_faces[0][0], m_box_faces[0][1], m_box_faces[0][2]); for (int i = 1; i < 6; i++) { - if (side*p.sideofPlane(boxFaces[i][0], boxFaces[i][1], boxFaces[i][2]) < 0) return false; + if (side*p.sideofPlane(m_box_faces[i][0], m_box_faces[i][1], m_box_faces[i][2]) < 0) return false; } return true; } diff --git a/src/tracks/quad.hpp b/src/tracks/quad.hpp index 80ded20db..951399697 100644 --- a/src/tracks/quad.hpp +++ b/src/tracks/quad.hpp @@ -61,7 +61,7 @@ private: /** Set if this quad should not be used by the AI. */ bool m_ai_ignore; - std::array , 6 > boxFaces; + Vec3 m_box_faces[6][4]; void findNormal(); From 4b36197787208d619ccde732522ded9f7cdb1ff0 Mon Sep 17 00:00:00 2001 From: nixt Date: Mon, 30 Jun 2014 16:42:20 +0530 Subject: [PATCH 048/350] Fixed reverse mode. --- src/tracks/graph_node.cpp | 48 +++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/src/tracks/graph_node.cpp b/src/tracks/graph_node.cpp index 08d63b577..955bea225 100644 --- a/src/tracks/graph_node.cpp +++ b/src/tracks/graph_node.cpp @@ -208,9 +208,18 @@ void GraphNode::getDistancesUnrolled(const Vec3 &xyz, const int fork_number, uns { const Quad& unrolled = getUnrolledQuad(fork_number,quad_idx); - Vec3 upper_center = 0.5f*(unrolled[2] + unrolled[3]), - lower_center = 0.5f*(unrolled[0] + unrolled[1]); - // center_line is from A to B, or lower_center to upper_center + Vec3 upper_center, lower_center; + if (!QuadGraph::get()->isReverse()) + { + upper_center = 0.5f*(unrolled[2] + unrolled[3]), + lower_center = 0.5f*(unrolled[0] + unrolled[1]); + } + else + { + upper_center = 0.5f*(unrolled[0] + unrolled[1]), + lower_center = 0.5f*(unrolled[2] + unrolled[3]); + } + // center_line is from A to B, or lower_center to upper_center core::vector3df A = lower_center.toIrrVector(), B = upper_center.toIrrVector(); result->setX(((B - A).crossProduct(xyz.toIrrVector() - A)).getLength() / (B - A).getLength()); @@ -308,22 +317,43 @@ void GraphNode::addUnrolledQuad(const GraphNode& next_node, int fork_number, int //m.setRotationCenter(next_quad_to_push.getCenter().toIrrVector(), // ((next_quad_to_push.getCenter() - 0.5f*(next_quad_to_push[0] + next_quad_to_push[1])) + 0.5f*(last_pushed_quad[2] + last_pushed_quad[3))); + for (unsigned int i = 0; i < 4; i++) m.rotateVect(new_points[i], next_quad_to_push[i].toIrrVector()); - Vec3 endEdge = (last_pushed_quad[2] - last_pushed_quad[3]); - Vec3 beginEdge = (new_points[1] - new_points[0]); - + Vec3 endEdge, beginEdge; + if (!QuadGraph::get()->isReverse()) + { + endEdge = (last_pushed_quad[2] - last_pushed_quad[3]); + beginEdge = (new_points[1] - new_points[0]); + } + else + { + endEdge = (last_pushed_quad[1] - last_pushed_quad[0]); + beginEdge = (new_points[2] - new_points[3]); + } + m.buildRotateFromTo(beginEdge.toIrrVector(), endEdge.toIrrVector()); for (unsigned int i = 0; i < 4; i++) m.rotateVect(new_points[i]); + + // Next translate the new quad to be pushed to the correct position infront // of the last quad in the vector of unrolled quads - Vec3 lower_center = 0.5f*(new_points[0] + new_points[1]); - Vec3 upper_center = 0.5f*(last_pushed_quad[2] + last_pushed_quad[3]); + Vec3 lower_center, upper_center; + if (!QuadGraph::get()->isReverse()) + { + lower_center = 0.5f*(new_points[0] + new_points[1]); + upper_center = 0.5f*(last_pushed_quad[2] + last_pushed_quad[3]); + } + else + { + lower_center = 0.5f*(new_points[2] + new_points[3]); + upper_center = 0.5f*(last_pushed_quad[0] + last_pushed_quad[1]); + } m.setTranslation((upper_center-lower_center).toIrrVector()); - //m.setTranslation((last_pushed_quad[3] - next_quad_to_push[0]).toIrrVector()); + for (unsigned int i = 0; i < 4; i++) m.translateVect(new_points[i]); From 1aaf3fbcc5be84207176c6d4b221b2018af09e18 Mon Sep 17 00:00:00 2001 From: nixt Date: Mon, 30 Jun 2014 16:43:28 +0530 Subject: [PATCH 049/350] Fix a bug in code related to alternate paths. --- src/karts/controller/skidding_ai.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index 62bf3ad3f..82e7b1f3d 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -2022,7 +2022,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) for (unsigned int j = 0; j < QuadGraph::get()->getNumberOfUnrolledQuads(); j++) { if (m_successor_index[current] != 0) - future_successor_idx = m_successor_index[m_track_node]; + future_successor_idx = m_successor_index[current]; current = m_next_node_index[current]; } From 4b303814fa606d42ac76c4a92b5d2291f33523e8 Mon Sep 17 00:00:00 2001 From: nixt Date: Mon, 30 Jun 2014 16:43:51 +0530 Subject: [PATCH 050/350] Some cleanup --- src/karts/controller/ai_base_controller.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/karts/controller/ai_base_controller.cpp b/src/karts/controller/ai_base_controller.cpp index e8eb0015a..2e2e7f10f 100644 --- a/src/karts/controller/ai_base_controller.cpp +++ b/src/karts/controller/ai_base_controller.cpp @@ -356,10 +356,7 @@ float AIBaseController::steerToPoint(const Vec3 &point) // First translate and rotate the point the AI is aiming // at into the kart's local coordinate system. - btQuaternion q(btVector3(0,1,0), -m_kart->getHeading()); - Vec3 p = point - m_kart->getXYZ(); - Vec3 lc = quatRotate(q, p); - + Vec3 lc; btTransform trans = m_kart->getTrans().inverse(); lc = trans(point); From 8011f5b40d1bc49dbc022214be89bb47ff2432ec Mon Sep 17 00:00:00 2001 From: nixt Date: Mon, 30 Jun 2014 17:41:20 +0530 Subject: [PATCH 051/350] Some smoothing to the changing gravity.. --- src/karts/kart.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index d8170fc26..93b8c4342 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1246,17 +1246,30 @@ void Kart::update(float dt) if (!m_flying) { float g = World::getWorld()->getTrack()->getGravity(); - Vec3 gravity(0.0f, -g, 0.0f); + Vec3 new_gravity(0.0f, -g, 0.0f); btRigidBody *body = getVehicle()->getRigidBody(); // If the material should overwrite the gravity, if (material->hasGravity() || 1) { Vec3 normal = m_terrain_info->getNormal(); - gravity = normal*-g; + new_gravity = normal*-g; //std::cout << gravity.x() << " "<< gravity.y() <<" " << gravity.z() << std::endl; } - body->setGravity(gravity); + + Vec3 old_gravity = body->getGravity(); + if (old_gravity != new_gravity) + { + float alpha = 0.98f; + new_gravity = alpha*old_gravity + (1 - alpha)*new_gravity; + body->setGravity(new_gravity); + } + else + { + body->setGravity(old_gravity); + } + + //body->setGravity(new_gravity); } // if !flying handleMaterialSFX(material); if (material->isDriveReset() && isOnGround()) From 64c229205187e416113ca1ffc8d0990a3537f040 Mon Sep 17 00:00:00 2001 From: nixt Date: Mon, 30 Jun 2014 19:18:48 +0530 Subject: [PATCH 052/350] Revert "Some smoothing to the changing gravity.." This reverts commit 8011f5b40d1bc49dbc022214be89bb47ff2432ec. --- src/karts/kart.cpp | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 4349da918..9915f3cd4 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1246,30 +1246,17 @@ void Kart::update(float dt) if (!m_flying) { float g = World::getWorld()->getTrack()->getGravity(); - Vec3 new_gravity(0.0f, -g, 0.0f); + Vec3 gravity(0.0f, -g, 0.0f); btRigidBody *body = getVehicle()->getRigidBody(); // If the material should overwrite the gravity, if (material->hasGravity() || 1) { Vec3 normal = m_terrain_info->getNormal(); - new_gravity = normal*-g; + gravity = normal*-g; //std::cout << gravity.x() << " "<< gravity.y() <<" " << gravity.z() << std::endl; } - - Vec3 old_gravity = body->getGravity(); - if (old_gravity != new_gravity) - { - float alpha = 0.98f; - new_gravity = alpha*old_gravity + (1 - alpha)*new_gravity; - body->setGravity(new_gravity); - } - else - { - body->setGravity(old_gravity); - } - - //body->setGravity(new_gravity); + body->setGravity(gravity); } // if !flying handleMaterialSFX(material); if (material->isDriveReset() && isOnGround()) From 184e45bb6defae66f586f8eade70fd1e480543fc Mon Sep 17 00:00:00 2001 From: nixt Date: Thu, 3 Jul 2014 11:13:49 +0530 Subject: [PATCH 053/350] Correct the landing after rescue for non-horizontal tracks. --- src/karts/kart.cpp | 12 +++++++++--- src/karts/rescue_animation.cpp | 4 +++- src/modes/linear_world.cpp | 15 +++++++++++++-- src/modes/world.cpp | 5 ++++- src/tracks/track.cpp | 7 ++++++- src/tracks/track_sector.cpp | 4 ++-- 6 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 9915f3cd4..73b340ea3 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1187,10 +1187,16 @@ void Kart::update(float dt) // But only do this if auto-rescue is enabled (i.e. it will be disabled in // battle mode), and the material the kart is driving on does not have // gravity (which can + + unsigned int sector = ((LinearWorld*)World::getWorld())->getTrackSector(getWorldKartId()).getCurrentGraphNode(); + const Vec3 quadNormal = QuadGraph::get()->getQuadOfNode(sector).getNormal(); + btQuaternion q = getTrans().getRotation(); + float roll = quadNormal.angle((Vec3(0, 1, 0).rotate(q.getAxis(), q.getAngle()))); + if(World::getWorld()->getTrack()->isAutoRescueEnabled() && (!m_terrain_info->getMaterial() || !m_terrain_info->getMaterial()->hasGravity()) && - !getKartAnimation() && fabs(getRoll())>60*DEGREE_TO_RAD && + !getKartAnimation() && fabs(roll)>60*DEGREE_TO_RAD && fabs(getSpeed())<3.0f ) { new RescueAnimation(this, /*is_auto_rescue*/true); @@ -1214,8 +1220,8 @@ void Kart::update(float dt) m_body->getBroadphaseHandle()->m_collisionFilterGroup = 0; } - unsigned int sector = ((LinearWorld*)World::getWorld())->getTrackSector(getWorldKartId()).getCurrentGraphNode(); - const Vec3 quadNormal = QuadGraph::get()->getQuadOfNode(sector).getNormal(); + + m_terrain_info->update(getXYZ() + epsilon*(quadNormal), -quadNormal); diff --git a/src/karts/rescue_animation.cpp b/src/karts/rescue_animation.cpp index 62330f421..a3a660e6c 100644 --- a/src/karts/rescue_animation.cpp +++ b/src/karts/rescue_animation.cpp @@ -97,7 +97,9 @@ RescueAnimation::~RescueAnimation() void RescueAnimation::update(float dt) { - m_xyz.setY(m_xyz.getY() + dt*m_velocity); + //m_xyz.setY(m_xyz.getY() + dt*m_velocity); + btQuaternion q1 = m_kart->getTrans().getRotation(); + m_xyz = m_xyz + dt*m_velocity*(Vec3(0, 1, 0).rotate(q1.getAxis(), q1.getAngle())); m_kart->setXYZ(m_xyz); m_curr_rotation += dt*m_add_rotation; btQuaternion q(m_curr_rotation.getHeading(), m_curr_rotation.getPitch(), diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index a7df70add..2b632ef3c 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -634,10 +634,21 @@ unsigned int LinearWorld::getRescuePositionIndex(AbstractKart *kart) btTransform LinearWorld::getRescueTransform(unsigned int index) const { const Vec3 &xyz = QuadGraph::get()->getQuadOfNode(index).getCenter(); + const Vec3 &normal = QuadGraph::get()->getQuadOfNode(index).getNormal(); btTransform pos; + + // First rotate into the quad's plane (q1), then rotate so that the kart points in the + // right direction (q2). + btQuaternion q1; + if (normal.cross(Vec3(0, 1, 0)).length() > 0) + { + q1 = btQuaternion(-normal.cross(Vec3(0, 1, 0)), normal.angle(Vec3(0, 1, 0))); + } + else q1 = btQuaternion(Vec3(0,1,0),0); + + btQuaternion q2(btVector3(normal), m_track->getAngle(index)); pos.setOrigin(xyz); - pos.setRotation(btQuaternion(btVector3(0.0f, 1.0f, 0.0f), - m_track->getAngle(index))); + pos.setRotation(q2*q1); return pos; } // getRescueTransform diff --git a/src/modes/world.cpp b/src/modes/world.cpp index af8ddb386..f099d7143 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -712,8 +712,11 @@ void World::moveKartTo(AbstractKart* kart, const btTransform &transform) { btTransform pos(transform); + btQuaternion rot = pos.getRotation(); + // Move the kart - Vec3 xyz = pos.getOrigin() + btVector3(0, 0.5f*kart->getKartHeight(),0.0f); + Vec3 xyz = pos.getOrigin() + btVector3(0, 0.55f*kart->getKartHeight(),0.0f). + rotate(rot.getAxis(),rot.getAngle()); pos.setOrigin(xyz); kart->setXYZ(xyz); diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index 02ecf8588..1f2123dc6 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -2388,7 +2388,12 @@ const core::vector3df& Track::getSunRotation() bool Track::findGround(AbstractKart *kart) { btVector3 to(kart->getXYZ()); - to.setY(-100000.f); + unsigned int sector = ((LinearWorld*)World::getWorld())->getTrackSector(kart->getWorldKartId()).getCurrentGraphNode(); + Vec3 quadNormal; + if (sector != QuadGraph::UNKNOWN_SECTOR) + quadNormal = QuadGraph::get()->getQuadOfNode(sector).getNormal(); + else quadNormal = Vec3(0, 1, 0); + to = to + -1000.0f*quadNormal; // Material and hit point are not needed; const Material *m; diff --git a/src/tracks/track_sector.cpp b/src/tracks/track_sector.cpp index 82d7cf09e..9ca3e7827 100644 --- a/src/tracks/track_sector.cpp +++ b/src/tracks/track_sector.cpp @@ -65,8 +65,8 @@ void TrackSector::update(const Vec3 &xyz) { // keep the current quad as the latest valid one IF the player has one // of the required checklines - const std::vector& checkline_requirements = - QuadGraph::get()->getNode(m_current_graph_node).getChecklineRequirements(); + GraphNode gn = QuadGraph::get()->getNode(m_current_graph_node); + const std::vector& checkline_requirements = gn.getChecklineRequirements(); if (checkline_requirements.size() == 0) { From 437f8191f45a5398088fe65ce9e2713bbaa1ce8e Mon Sep 17 00:00:00 2001 From: nixt Date: Thu, 3 Jul 2014 17:24:04 +0530 Subject: [PATCH 054/350] Fix a bug where the kart's wheels would intersect with the road when rescued to an upside location. --- src/modes/world.cpp | 2 +- src/tracks/track.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/modes/world.cpp b/src/modes/world.cpp index f099d7143..212b0df33 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -715,7 +715,7 @@ void World::moveKartTo(AbstractKart* kart, const btTransform &transform) btQuaternion rot = pos.getRotation(); // Move the kart - Vec3 xyz = pos.getOrigin() + btVector3(0, 0.55f*kart->getKartHeight(),0.0f). + Vec3 xyz = pos.getOrigin() + btVector3(0, 0.5f*kart->getKartHeight(),0.0f). rotate(rot.getAxis(),rot.getAngle()); pos.setOrigin(xyz); diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index 1f2123dc6..48c0ae842 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -2431,6 +2431,7 @@ bool Track::findGround(AbstractKart *kart) btTransform t = kart->getBody()->getCenterOfMassTransform(); + btQuaternion q = t.getRotation(); // The computer offset is slightly too large, it should take // the default suspension rest insteat of suspension rest (i.e. the // length of the suspension with the weight of the kart resting on @@ -2438,7 +2439,7 @@ bool Track::findGround(AbstractKart *kart) // - so I'll leave it in for now. float offset = kart->getKartProperties()->getSuspensionRest() + kart->getKartProperties()->getWheelRadius(); - t.setOrigin(hit_point+Vec3(0, offset, 0) ); + t.setOrigin(hit_point+ (btVector3(0, offset, 0).rotate(q.getAxis(),q.getAngle()) ) ); kart->getBody()->setCenterOfMassTransform(t); kart->setTrans(t); From dbe4cd02fab4fb69324bc3d08e39b4d24c6cd0a6 Mon Sep 17 00:00:00 2001 From: nixt Date: Fri, 4 Jul 2014 12:43:34 +0530 Subject: [PATCH 055/350] First attempt at correcting the rescue animation. --- src/karts/rescue_animation.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/karts/rescue_animation.cpp b/src/karts/rescue_animation.cpp index a3a660e6c..3a2d7d50f 100644 --- a/src/karts/rescue_animation.cpp +++ b/src/karts/rescue_animation.cpp @@ -45,10 +45,26 @@ RescueAnimation::RescueAnimation(AbstractKart *kart, bool is_auto_rescue) m_kart->getAttachment()->clear(); + // We get the final transform of the kart so that it is rotated + // accordingly during the rescue animation + World * world = World::getWorld(); + unsigned int index = world->getRescuePositionIndex(m_kart); + btTransform t = world->getRescueTransform(index); + Vec3 up = t.getBasis().getColumn(1); + float target_pitch = atan2(up.getZ(), fabsf(up.getY())); + float target_roll = atan2(up.getX(), up.getY()); + + m_curr_rotation.setPitch(m_kart->getPitch()); - m_curr_rotation.setRoll(m_kart->getRoll() ); + m_curr_rotation.setRoll(m_kart->getRoll()); m_curr_rotation.setHeading(0); - m_add_rotation = -m_curr_rotation/m_timer; + + Vec3 required_rotation; + required_rotation.setPitch(target_pitch - m_kart->getPitch()); + required_rotation.setRoll(target_roll - m_kart->getRoll()); + required_rotation.setHeading(0); + m_add_rotation = -required_rotation/m_timer; + m_curr_rotation.setHeading(m_kart->getHeading()); // Add a hit unless it was auto-rescue From 2985974f8339b8f3eff63b0419aa0d8584231c3f Mon Sep 17 00:00:00 2001 From: nixt Date: Fri, 4 Jul 2014 21:40:23 +0530 Subject: [PATCH 056/350] Tilt items on track --- src/tracks/track.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index 48c0ae842..b4fb2211c 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -2301,11 +2301,13 @@ void Track::itemCommand(const XMLNode *node) #endif } - // Don't tilt the items, since otherwise the rotation will look odd, - // i.e. the items will not rotate around the normal, but 'wobble' - // around. - //Vec3 normal(0.7071f, 0, 0.7071f); - Vec3 normal(0, 1, 0); + // Tilt the items according to the track + int road_sector = QuadGraph::UNKNOWN_SECTOR; + QuadGraph::get()->findRoadSector(xyz, &road_sector); + // If a valid road_sector is not found + if (road_sector == QuadGraph::UNKNOWN_SECTOR) + road_sector=QuadGraph::get()->findOutOfRoadSector(xyz, road_sector); + Vec3 normal = QuadGraph::get()->getQuadOfNode(road_sector).getNormal(); ItemManager::get()->newItem(type, loc, normal); } // itemCommand From 5f15138247b270de7810440d3e3e738bf08dd049 Mon Sep 17 00:00:00 2001 From: nixt Date: Fri, 4 Jul 2014 21:41:12 +0530 Subject: [PATCH 057/350] Correct spatialToTrack and getDistances to work in 3D. --- src/tracks/graph_node.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/tracks/graph_node.cpp b/src/tracks/graph_node.cpp index 955bea225..2732326e2 100644 --- a/src/tracks/graph_node.cpp +++ b/src/tracks/graph_node.cpp @@ -194,14 +194,13 @@ void GraphNode::setDirectionData(unsigned int successor, DirectionType dir, */ void GraphNode::getDistances(const Vec3 &xyz, Vec3 *result) { - core::vector2df xyz2d(xyz.getX(), xyz.getZ()); - core::vector2df closest = m_line.getClosestPoint(xyz2d); - if(m_line.getPointOrientation(xyz2d)>0) - result->setX( (closest-xyz2d).getLength()); // to the right - else - result->setX(-(closest-xyz2d).getLength()); // to the left + + // center_line is from A to B, or lower_center to upper_center + core::vector3df A = m_lower_center.toIrrVector(), B = m_upper_center.toIrrVector(); + result->setX(((B - A).crossProduct(xyz.toIrrVector() - A)).getLength() / (B - A).getLength()); + result->setZ( m_distance_from_start + - (closest-m_lower_center_2d).getLength()); + (xyz-m_lower_center).length()); } // getDistances void GraphNode::getDistancesUnrolled(const Vec3 &xyz, const int fork_number, unsigned int quad_idx, Vec3 *result) @@ -219,7 +218,7 @@ void GraphNode::getDistancesUnrolled(const Vec3 &xyz, const int fork_number, uns upper_center = 0.5f*(unrolled[0] + unrolled[1]), lower_center = 0.5f*(unrolled[2] + unrolled[3]); } - // center_line is from A to B, or lower_center to upper_center + core::vector3df A = lower_center.toIrrVector(), B = upper_center.toIrrVector(); result->setX(((B - A).crossProduct(xyz.toIrrVector() - A)).getLength() / (B - A).getLength()); From 9220d50035b4f43e9aa4a51a4c7d05e5a5fcce62 Mon Sep 17 00:00:00 2001 From: nixt Date: Wed, 9 Jul 2014 00:09:48 +0530 Subject: [PATCH 058/350] Revert "Correct spatialToTrack and getDistances to work in 3D." This reverts commit 5f15138247b270de7810440d3e3e738bf08dd049. --- src/tracks/graph_node.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/tracks/graph_node.cpp b/src/tracks/graph_node.cpp index 2732326e2..955bea225 100644 --- a/src/tracks/graph_node.cpp +++ b/src/tracks/graph_node.cpp @@ -194,13 +194,14 @@ void GraphNode::setDirectionData(unsigned int successor, DirectionType dir, */ void GraphNode::getDistances(const Vec3 &xyz, Vec3 *result) { - - // center_line is from A to B, or lower_center to upper_center - core::vector3df A = m_lower_center.toIrrVector(), B = m_upper_center.toIrrVector(); - result->setX(((B - A).crossProduct(xyz.toIrrVector() - A)).getLength() / (B - A).getLength()); - + core::vector2df xyz2d(xyz.getX(), xyz.getZ()); + core::vector2df closest = m_line.getClosestPoint(xyz2d); + if(m_line.getPointOrientation(xyz2d)>0) + result->setX( (closest-xyz2d).getLength()); // to the right + else + result->setX(-(closest-xyz2d).getLength()); // to the left result->setZ( m_distance_from_start + - (xyz-m_lower_center).length()); + (closest-m_lower_center_2d).getLength()); } // getDistances void GraphNode::getDistancesUnrolled(const Vec3 &xyz, const int fork_number, unsigned int quad_idx, Vec3 *result) @@ -218,7 +219,7 @@ void GraphNode::getDistancesUnrolled(const Vec3 &xyz, const int fork_number, uns upper_center = 0.5f*(unrolled[0] + unrolled[1]), lower_center = 0.5f*(unrolled[2] + unrolled[3]); } - + // center_line is from A to B, or lower_center to upper_center core::vector3df A = lower_center.toIrrVector(), B = upper_center.toIrrVector(); result->setX(((B - A).crossProduct(xyz.toIrrVector() - A)).getLength() / (B - A).getLength()); From 504360ef7ed6bbca1ffafd61515b57a0a8def79f Mon Sep 17 00:00:00 2001 From: nixt Date: Wed, 9 Jul 2014 22:56:01 +0530 Subject: [PATCH 059/350] Correct getDistance and m_line for 3D. Line joining lower_center to upper_center of quad is now a 3D line. getDistances is now fixed to return the correct distance of a point from m_line and with the correct direction. --- src/tracks/graph_node.cpp | 23 ++++++++++++----------- src/tracks/graph_node.hpp | 2 +- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/tracks/graph_node.cpp b/src/tracks/graph_node.cpp index 955bea225..4803f6f14 100644 --- a/src/tracks/graph_node.cpp +++ b/src/tracks/graph_node.cpp @@ -62,8 +62,8 @@ GraphNode::GraphNode(unsigned int quad_index, unsigned int node_index) m_lower_center = (quad[0]+quad[1]) * 0.5f; m_upper_center = (quad[2]+quad[3]) * 0.5f; } - m_line = core::line2df(m_upper_center.getX(), m_upper_center.getZ(), - m_lower_center.getX(), m_lower_center.getZ() ); + m_line = core::line3df(m_lower_center.toIrrVector(), + m_upper_center.toIrrVector()); // Only this 2d point is needed later m_lower_center_2d = core::vector2df(m_lower_center.getX(), m_lower_center.getZ() ); @@ -194,14 +194,16 @@ void GraphNode::setDirectionData(unsigned int successor, DirectionType dir, */ void GraphNode::getDistances(const Vec3 &xyz, Vec3 *result) { - core::vector2df xyz2d(xyz.getX(), xyz.getZ()); - core::vector2df closest = m_line.getClosestPoint(xyz2d); - if(m_line.getPointOrientation(xyz2d)>0) - result->setX( (closest-xyz2d).getLength()); // to the right + core::vector3df xyz_irr = xyz.toIrrVector(); + core::vector3df closest = m_line.getClosestPoint(xyz.toIrrVector()); + core::vector3df normal = getQuad().getNormal().toIrrVector(); + + if(xyz.sideofPlane(closest, closest+normal, m_line.end)<0) + result->setX( (closest-xyz_irr).getLength()); // to the right else - result->setX(-(closest-xyz2d).getLength()); // to the left + result->setX(-(closest-xyz_irr).getLength()); // to the left result->setZ( m_distance_from_start + - (closest-m_lower_center_2d).getLength()); + (closest-m_lower_center.toIrrVector()).getLength()); } // getDistances void GraphNode::getDistancesUnrolled(const Vec3 &xyz, const int fork_number, unsigned int quad_idx, Vec3 *result) @@ -236,9 +238,8 @@ void GraphNode::getDistancesUnrolled(const Vec3 &xyz, const int fork_number, uns */ float GraphNode::getDistance2FromPoint(const Vec3 &xyz) { - core::vector2df xyz2d(xyz.getX(), xyz.getZ()); - core::vector2df closest = m_line.getClosestPoint(xyz2d); - return (closest-xyz2d).getLengthSQ(); + core::vector3df closest = m_line.getClosestPoint(xyz.toIrrVector()); + return (closest-xyz.toIrrVector()).getLengthSQ(); } // getDistance2FromPoint // ---------------------------------------------------------------------------- diff --git a/src/tracks/graph_node.hpp b/src/tracks/graph_node.hpp index af1f109a1..5aafd4ec7 100644 --- a/src/tracks/graph_node.hpp +++ b/src/tracks/graph_node.hpp @@ -95,7 +95,7 @@ private: * taller karts would have a larger distance from the center. It also * saves computation, and it is only needed to determine the distance * from the center of the drivelines anyway. */ - core::line2df m_line; + core::line3df m_line; typedef std::vector PathToNodeVector; /** This vector is only used if the graph node has more than one From 523d38c76bdf9a1d2459f5e52b685fc32b22b92d Mon Sep 17 00:00:00 2001 From: nixt Date: Wed, 9 Jul 2014 23:02:25 +0530 Subject: [PATCH 060/350] Change hitLine so that it checks for closeness with a 3D line. --- src/items/item.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/items/item.hpp b/src/items/item.hpp index df6125197..d29356595 100644 --- a/src/items/item.hpp +++ b/src/items/item.hpp @@ -223,12 +223,12 @@ protected: * \param line The line segment which is tested if it is close enough * to this item so that this item would be collected. */ - bool hitLine(const core::line2df &line, + bool hitLine(const core::line3df &line, const AbstractKart *kart=NULL) const { if(m_event_handler==kart && m_deactive_time >0) return false; - core::vector2df p2d = m_xyz.toIrrVector2d(); - core::vector2df closest = line.getClosestPoint(p2d); + + Vec3 closest = line.getClosestPoint(m_xyz.toIrrVector()); return hitKart(closest, kart); } // hitLine From 2556ef224673e9a6c6ce219879fb3ba518785111 Mon Sep 17 00:00:00 2001 From: nixt Date: Wed, 9 Jul 2014 23:07:17 +0530 Subject: [PATCH 061/350] Increase value of m_distance_2 in Item.hpp (needs testing/tweaking) Increase value of m_distance_2 because distances from a 3D line as checked in hitLine() can be greater but the kart may still hit the item. This may need to be tweaked. --- src/items/item.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/items/item.cpp b/src/items/item.cpp index 2a15d4399..a2af7dd2c 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -37,7 +37,7 @@ Item::Item(ItemType type, const Vec3& xyz, const Vec3& normal, { assert(type != ITEM_TRIGGER); // use other constructor for that - m_distance_2 = 0.8f; + m_distance_2 = 1.2f; initItem(type, xyz); // Sets heading to 0, and sets pitch and roll depending on the normal. */ m_original_hpr = Vec3(0, normal); From 6fc144dd8a9cb07e6bec0adbbed84254a5860169 Mon Sep 17 00:00:00 2001 From: nixt Date: Wed, 9 Jul 2014 23:22:20 +0530 Subject: [PATCH 062/350] Item collection and avoidance now works with 3D tracks! Also improved AI steering where it would cut corners too aggressively. --- src/karts/controller/skidding_ai.cpp | 125 ++++++++++++++++++--------- src/karts/controller/skidding_ai.hpp | 6 +- 2 files changed, 88 insertions(+), 43 deletions(-) diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index 82e7b1f3d..d0d75aeb8 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -626,6 +626,8 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, float kart_aim_angle = atan2(aim_point->getX()-m_kart->getXYZ().getX(), aim_point->getZ()-m_kart->getXYZ().getZ()); + Vec3 kart_aim_direction = *aim_point - m_kart->getXYZ(); + // Make sure we have a valid last_node if(last_node==QuadGraph::UNKNOWN_SECTOR) last_node = m_next_node_index[m_track_node]; @@ -645,7 +647,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, ItemManager::get()->getItemsInQuads(q_index); for(unsigned int i=0; igetDistanceToNext(node, @@ -662,6 +664,9 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, m_kart->getXYZ().getX(), m_kart->getXYZ().getZ()); + core::line3df line_to_target_3d((*aim_point).toIrrVector(), + m_kart->getXYZ().toIrrVector()); + // 2) If the kart is aiming for an item, but (suddenly) detects // some close-by items to avoid (e.g. behind the item, which was too // far away to be considered earlier), the kart cancels collecting @@ -672,7 +677,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, for(unsigned int i=0; i< items_to_avoid.size(); i++) { Vec3 d = items_to_avoid[i]->getXYZ()-m_item_to_collect->getXYZ(); - if( d.length2_2d()>m_ai_properties->m_bad_item_closeness_2) + if( d.length2()>m_ai_properties->m_bad_item_closeness_2) continue; // It could make sense to also test if the bad item would // actually be hit, not only if it is close (which can result @@ -692,7 +697,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, // ------------------------------------- if(m_item_to_collect) { - if(handleSelectedItem(kart_aim_angle, aim_point)) + if(handleSelectedItem(kart_aim_direction, aim_point)) { // Still aim at the previsouly selected item. *aim_point = m_item_to_collect->getXYZ(); @@ -717,7 +722,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, { // If we need to steer to avoid an item, this takes priority, // ignore items to collect and return the new aim_point. - if(steerToAvoid(items_to_avoid, line_to_target, aim_point)) + if(steerToAvoid(items_to_avoid, line_to_target_3d, aim_point)) { #ifdef AI_DEBUG m_item_sphere->setVisible(true); @@ -766,7 +771,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, // it's on a good enough driveline, so make this item a permanent // target. Otherwise only try to get closer (till hopefully this // item s on our driveline) - if(item_to_collect->hitLine(line_to_target, m_kart)) + if(item_to_collect->hitLine(line_to_target_3d, m_kart)) { #ifdef AI_DEBUG m_item_sphere->setVisible(true); @@ -784,9 +789,15 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, // Kart will not hit item, try to get closer to this item // so that it can potentially become a permanent target. Vec3 xyz = item_to_collect->getXYZ(); - float item_angle = atan2(xyz.getX() - m_kart->getXYZ().getX(), - xyz.getZ() - m_kart->getXYZ().getZ()); - float angle = normalizeAngle(kart_aim_angle - item_angle); + Vec3 item_direction = xyz - m_kart->getXYZ(); + Vec3 plane_normal = QuadGraph::get()->getQuadOfNode(m_track_node) + .getNormal(); + float dist_to_plane = item_direction.dot(plane_normal); + Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; + + float angle_to_item = (projected_xyz - m_kart->getXYZ()) + .angle(kart_aim_direction); + float angle = normalizeAngle(angle_to_item); if(fabsf(angle) < 0.3) { @@ -827,8 +838,8 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, bool SkiddingAI::hitBadItemWhenAimAt(const Item *item, const std::vector &items_to_avoid) { - core::line2df to_item(m_kart->getXYZ().getX(), m_kart->getXYZ().getZ(), - item->getXYZ().getX(), item->getXYZ().getZ() ); + core::line3df to_item(m_kart->getXYZ().toIrrVector(), + item->getXYZ().toIrrVector()); for(unsigned int i=0; ihitLine(to_item, m_kart)) @@ -849,7 +860,7 @@ bool SkiddingAI::hitBadItemWhenAimAt(const Item *item, * \param last_node * \return True if th AI should still aim for the pre-selected item. */ -bool SkiddingAI::handleSelectedItem(float kart_aim_angle, Vec3 *aim_point) +bool SkiddingAI::handleSelectedItem(Vec3 kart_aim_direction, Vec3 *aim_point) { // If the item is unavailable or has been switched into a bad item // stop aiming for it. @@ -857,11 +868,22 @@ bool SkiddingAI::handleSelectedItem(float kart_aim_angle, Vec3 *aim_point) m_item_to_collect->getType() == Item::ITEM_BANANA ) return false; + // Project the item's location onto the plane of the current quad. + // This is necessary because the kart's aim point may not be on the track + // in 3D curves. So we project the item's location onto the plane in which + // the kart is. The current quad provides a good estimate of the kart's plane. const Vec3 &xyz = m_item_to_collect->getXYZ(); - float item_angle = atan2(xyz.getX() - m_kart->getXYZ().getX(), - xyz.getZ() - m_kart->getXYZ().getZ()); + Vec3 item_direction = xyz - m_kart->getXYZ(); + Vec3 plane_normal = QuadGraph::get()->getQuadOfNode(m_track_node) + .getNormal(); + float dist_to_plane = item_direction.dot(plane_normal); + Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; + + float angle_to_item = (projected_xyz - m_kart->getXYZ()) + .angle(kart_aim_direction); + + float angle = normalizeAngle(angle_to_item); - float angle = normalizeAngle(kart_aim_angle - item_angle); if(fabsf(angle)>1.5) { // We (likely) have passed the item we were aiming for @@ -890,7 +912,7 @@ bool SkiddingAI::handleSelectedItem(float kart_aim_angle, Vec3 *aim_point) * \return True if steering is necessary to avoid an item. */ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, - const core::line2df &line_to_target, + const core::line3df &line_to_target, Vec3 *aim_point) { // First determine the left-most and right-most item. @@ -916,14 +938,22 @@ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, // Check if we would drive left of the leftmost or right of the // rightmost point - if so, nothing to do. - core::vector2df left(items_to_avoid[index_left_most]->getXYZ().getX(), - items_to_avoid[index_left_most]->getXYZ().getZ()); + + //core::vector2df left(items_to_avoid[index_left_most]->getXYZ().getX(), + // items_to_avoid[index_left_most]->getXYZ().getZ()); + Vec3 left(items_to_avoid[index_left_most]->getXYZ()); + int node_index = items_to_avoid[index_left_most]->getGraphNode(); + Vec3 normal = QuadGraph::get()->getQuadOfNode(node_index).getNormal(); + Vec3 p1 = line_to_target.start, + p2 = line_to_target.getMiddle() + normal.toIrrVector(), + p3 = line_to_target.end; + int item_index = -1; bool is_left = false; // >=0 means the point is to the right of the line, or the line is // to the left of the point. - if(line_to_target.getPointOrientation(left)>=0) + if(left.sideofPlane(p1,p2,p3) <= 0) { // Left of leftmost point item_index = index_left_most; @@ -931,9 +961,16 @@ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, } else { - core::vector2df right(items_to_avoid[index_right_most]->getXYZ().getX(), - items_to_avoid[index_right_most]->getXYZ().getZ()); - if(line_to_target.getPointOrientation(right)<=0) + //core::vector2df right(items_to_avoid[index_right_most]->getXYZ().getX(), + // items_to_avoid[index_right_most]->getXYZ().getZ()); + Vec3 left(items_to_avoid[index_left_most]->getXYZ()); + int node_index = items_to_avoid[index_left_most]->getGraphNode(); + Vec3 normal = QuadGraph::get()->getQuadOfNode(node_index).getNormal(); + Vec3 p1 = line_to_target.start, + p2 = line_to_target.getMiddle() + normal.toIrrVector(), + p3 = line_to_target.end; + + if (left.sideofPlane(p1, p2, p3) >= 0) { // Right of rightmost point item_index = index_right_most; @@ -974,20 +1011,20 @@ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, float min_distance[2] = {99999.9f, 99999.9f}; int index[2] = {-1, -1}; - core::vector2df closest2d[2]; + core::vector3df closest3d[2]; for(unsigned int i=0; igetXYZ(); core::vector2df item2d = xyz.toIrrVector2d(); - core::vector2df point2d = line_to_target.getClosestPoint(item2d); - float d = (xyz.toIrrVector2d() - point2d).getLengthSQ(); - float direction = line_to_target.getPointOrientation(item2d); - int ind = direction<0 ? 0 : 1; + core::vector3df point3d = line_to_target.getClosestPoint(xyz.toIrrVector()); + float d = (xyz.toIrrVector() - point3d).getLengthSQ(); + float direction = xyz.sideofPlane(p1,p2,p3); + int ind = direction<0 ? 1 : 0; if(d &items_to_avoid, // We are driving between item_to_avoid[index[0]] and ...[1]. // If we don't hit any of them, just keep on driving as normal - bool hit_left = items_to_avoid[index[0]]->hitKart(closest2d[0], m_kart); - bool hit_right = items_to_avoid[index[1]]->hitKart(closest2d[1], m_kart); + bool hit_left = items_to_avoid[index[0]]->hitKart(closest3d[0], m_kart); + bool hit_right = items_to_avoid[index[1]]->hitKart(closest3d[1], m_kart); if( !hit_left && !hit_right) return false; @@ -1032,7 +1069,7 @@ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, * (NULL if no item was avoided so far). * \param item_to_collect A pointer to a previously selected item to collect. */ -void SkiddingAI::evaluateItems(const Item *item, float kart_aim_angle, +void SkiddingAI::evaluateItems(const Item *item, Vec3 kart_aim_direction, std::vector *items_to_avoid, std::vector *items_to_collect) { @@ -1081,13 +1118,21 @@ void SkiddingAI::evaluateItems(const Item *item, float kart_aim_angle, // to avoid are collected). if(!avoid) { - // item_angle The angle of the item (relative to the forward axis, - // so 0 means straight ahead in world coordinates!). + // Project the item's location onto the plane of the current quad. + // This is necessary because the kart's aim point may not be on the track + // in 3D curves. So we project the item's location onto the plane in which + // the kart is. The current quad provides a good estimate of the kart's plane. const Vec3 &xyz = item->getXYZ(); - float item_angle = atan2(xyz.getX() - m_kart->getXYZ().getX(), - xyz.getZ() - m_kart->getXYZ().getZ()); + Vec3 item_direction = xyz - m_kart->getXYZ(); + Vec3 plane_normal = QuadGraph::get()->getQuadOfNode(m_track_node) + .getNormal(); + float dist_to_plane = item_direction.dot(plane_normal); + Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; + + float angle_to_item = (projected_xyz - m_kart->getXYZ()) + .angle(kart_aim_direction); - float diff = normalizeAngle(kart_aim_angle-item_angle); + float diff = normalizeAngle(angle_to_item); // The kart is driving at high speed, when the current max speed // is higher than the max speed of the kart (which is caused by @@ -1116,14 +1161,14 @@ void SkiddingAI::evaluateItems(const Item *item, float kart_aim_angle, else list = items_to_collect; - float new_distance = (item->getXYZ() - m_kart->getXYZ()).length2_2d(); + float new_distance = (item->getXYZ() - m_kart->getXYZ()).length2(); // This list is usually very short, so use a simple bubble sort list->push_back(item); int i; for(i=list->size()-2; i>=0; i--) { - float d = ((*list)[i]->getXYZ() - m_kart->getXYZ()).length2_2d(); + float d = ((*list)[i]->getXYZ() - m_kart->getXYZ()).length2(); if(d<=new_distance) { break; @@ -2089,7 +2134,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) //If we are outside, the previous node is what we are looking for if ( distance + m_kart_width * 0.5f - > QuadGraph::get()->getNode((*last_node+j)%QuadGraph::get()->getNumNodes() ).getPathWidth() ) + > QuadGraph::get()->getNode((*last_node+j)%QuadGraph::get()->getNumNodes() ).getPathWidth()*0.5f ) { *aim_position = QuadGraph::get()->getUnrolledQuadOfNode(m_track_node,future_successor_idx,j) .getCenter(); @@ -2098,8 +2143,8 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) } - // angle = angle1; - //*last_node = target_sector; + angle = angle1; + *last_node = target_sector; } // for i<100 *aim_position = QuadGraph::get()->getNode(m_track_node). getUnrolledQuad(future_successor_idx,QuadGraph::get()->getNumberOfUnrolledQuads()).getCenter(); diff --git a/src/karts/controller/skidding_ai.hpp b/src/karts/controller/skidding_ai.hpp index 4b87ac9d1..a6673b3bd 100644 --- a/src/karts/controller/skidding_ai.hpp +++ b/src/karts/controller/skidding_ai.hpp @@ -228,13 +228,13 @@ private: void computeNearestKarts(); void handleItemCollectionAndAvoidance(Vec3 *aim_point, int last_node); - bool handleSelectedItem(float kart_aim_angle, Vec3 *aim_point); + bool handleSelectedItem(Vec3 kart_aim_direction, Vec3 *aim_point); bool steerToAvoid(const std::vector &items_to_avoid, - const core::line2df &line_to_target, + const core::line3df &line_to_target, Vec3 *aim_point); bool hitBadItemWhenAimAt(const Item *item, const std::vector &items_to_avoid); - void evaluateItems(const Item *item, float kart_aim_angle, + void evaluateItems(const Item *item, Vec3 kart_aim_direction, std::vector *items_to_avoid, std::vector *items_to_collect); From c7d4a8c85c75b92887c1cec57a46b3db1d60bbed Mon Sep 17 00:00:00 2001 From: nixt Date: Thu, 10 Jul 2014 00:38:14 +0530 Subject: [PATCH 063/350] Disabled AI_DEBUG because its currently not supported. --- src/graphics/irr_driver.cpp | 4 ++-- src/karts/controller/skidding_ai.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index c00dabf44..28d0ad433 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -447,9 +447,9 @@ void IrrDriver::initDevice() Log::info("IrrDriver", "OpenGL vendor: %s", glGetString(GL_VENDOR)); Log::info("IrrDriver", "OpenGL renderer: %s", glGetString(GL_RENDERER)); Log::info("IrrDriver", "OpenGL version string: %s", glGetString(GL_VERSION)); - //m_glsl = (GLMajorVersion > 3 || (GLMajorVersion == 3 && GLMinorVersion >= 1)); + m_glsl = (GLMajorVersion > 3 || (GLMajorVersion == 3 && GLMinorVersion >= 1)); } - m_glsl = false; + //m_glsl = false; // Parse extensions hasVSLayer = false; diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index d0d75aeb8..d904f2687 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -35,7 +35,7 @@ #endif #include "karts/controller/skidding_ai.hpp" -#define AI_DEBUG + #ifdef AI_DEBUG # include "graphics/irr_driver.hpp" #endif From cf354e2828f5d2c0446c31b6f6094a503992da3d Mon Sep 17 00:00:00 2001 From: nixt Date: Tue, 15 Jul 2014 13:39:19 +0530 Subject: [PATCH 064/350] set m_glsl = false; because AI_DEBUG not supported in the new pipeline. --- src/graphics/irr_driver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 28d0ad433..d83c5c3ea 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -449,7 +449,7 @@ void IrrDriver::initDevice() Log::info("IrrDriver", "OpenGL version string: %s", glGetString(GL_VERSION)); m_glsl = (GLMajorVersion > 3 || (GLMajorVersion == 3 && GLMinorVersion >= 1)); } - //m_glsl = false; + m_glsl = false; // Parse extensions hasVSLayer = false; From 8d7f09e03792c3fff7dba6fbb430d6983a205cb9 Mon Sep 17 00:00:00 2001 From: nixt Date: Wed, 16 Jul 2014 10:58:40 +0530 Subject: [PATCH 065/350] Update bowling ball to follow the track. Also updated cake, but it needs more work. --- src/items/bowling.cpp | 27 ++++++++++++++----- src/items/cake.cpp | 14 ++++++++-- src/items/flyable.cpp | 56 ++++++++++++++++++++++++++++++++------- src/items/flyable.hpp | 2 +- src/items/plunger.cpp | 4 +-- src/items/rubber_ball.cpp | 4 +-- src/karts/kart.cpp | 9 +++++-- 7 files changed, 92 insertions(+), 24 deletions(-) diff --git a/src/items/bowling.cpp b/src/items/bowling.cpp index b7fe73699..e5a089946 100644 --- a/src/items/bowling.cpp +++ b/src/items/bowling.cpp @@ -24,7 +24,9 @@ #include "graphics/material.hpp" #include "io/xml_node.hpp" #include "karts/abstract_kart.hpp" +#include "modes/linear_world.hpp" #include "utils/random_generator.hpp" +#include "tracks/quad_graph.hpp" #include "utils/log.hpp" //TODO: remove after debugging is done @@ -55,11 +57,23 @@ Bowling::Bowling(AbstractKart *kart) if(m_speed < min_speed) m_speed = min_speed; } + Vec3 quadNormal; + if (race_manager->getMinorMode() != RaceManager::MINOR_MODE_3_STRIKES && + race_manager->getMinorMode() != RaceManager::MINOR_MODE_SOCCER) + { + unsigned int sector = ((LinearWorld*)World::getWorld())-> + getTrackSector(kart->getWorldKartId()).getCurrentGraphNode(); + quadNormal = QuadGraph::get()->getQuadOfNode(sector).getNormal(); + } + else + quadNormal = btVector3(.0f, 1.0f, .0f); + createPhysics(y_offset, btVector3(0.0f, 0.0f, m_speed*2), - new btSphereShape(0.5f*m_extend.getY()), - 1.0f /*restitution*/, - -70.0f /*gravity*/, - true /*rotates*/); + new btSphereShape(0.5f*m_extend.getY()), + 1.0f /*restitution*/, + -70.0f*quadNormal /*gravity*/, + true /*rotates*/); + // Even if the ball is fired backwards, m_speed must be positive, // otherwise the ball can start to vibrate when energy is added. m_speed = fabsf(m_speed); @@ -136,14 +150,15 @@ bool Bowling::updateAndDelete(float dt) m_body->applyCentralForce(direction); } } - + + // Bowling balls lose energy (e.g. when hitting the track), so increase // the speed if the ball is too slow, but only if it's not too high (if // the ball is too high, it is 'pushed down', which can reduce the // speed, which causes the speed to increase, which in turn causes // the ball to fly higher and higher. //btTransform trans = getTrans(); - float hat = getXYZ().getY()-getHoT(); + float hat = (getXYZ() - getHitPoint()).length(); if(hat-0.5f*m_extend.getY()<0.01f) { const Material *material = getMaterial(); diff --git a/src/items/cake.cpp b/src/items/cake.cpp index 7f802dcbb..51d48c1f2 100644 --- a/src/items/cake.cpp +++ b/src/items/cake.cpp @@ -35,6 +35,15 @@ Cake::Cake (AbstractKart *kart) : Flyable(kart, PowerupManager::POWERUP_CAKE) { m_target = NULL; + Vec3 gravity_vector; + if (race_manager->getMinorMode() != RaceManager::MINOR_MODE_3_STRIKES && + race_manager->getMinorMode() != RaceManager::MINOR_MODE_SOCCER) + { + gravity_vector = kart->getBody()->getGravity(); + gravity_vector = gravity_vector.normalize() * m_gravity; + } + else + gravity_vector = Vec3(0.0f, -1.0f, 0.0f) * m_gravity; // A bit of a hack: the mass of this kinematic object is still 1.0 // (see flyable), which enables collisions. I tried setting // collisionFilterGroup/mask, but still couldn't get this object to @@ -91,7 +100,7 @@ Cake::Cake (AbstractKart *kart) : Flyable(kart, PowerupManager::POWERUP_CAKE) createPhysics(forward_offset, m_initial_velocity, new btCylinderShape(0.5f*m_extend), - 0.5f /* restitution */, -m_gravity, + 0.5f /* restitution */, btVector3(gravity_vector), true /* rotation */, false /* backwards */, &trans); } else @@ -105,7 +114,7 @@ Cake::Cake (AbstractKart *kart) : Flyable(kart, PowerupManager::POWERUP_CAKE) createPhysics(forward_offset, m_initial_velocity, new btCylinderShape(0.5f*m_extend), - 0.5f /* restitution */, -m_gravity, + 0.5f /* restitution */, btVector3(gravity_vector), true /* rotation */, backwards, &trans); } @@ -128,6 +137,7 @@ void Cake::init(const XMLNode &node, scene::IMesh *cake_model) { Flyable::init(node, cake_model, PowerupManager::POWERUP_CAKE); float max_distance = 80.0f; + m_gravity = 9.8f; if (m_gravity < 0) m_gravity *= -1.0f; diff --git a/src/items/flyable.cpp b/src/items/flyable.cpp index e7b5d3ea1..d3b4f2100 100644 --- a/src/items/flyable.cpp +++ b/src/items/flyable.cpp @@ -34,7 +34,7 @@ #include "items/projectile_manager.hpp" #include "karts/abstract_kart.hpp" #include "karts/explosion_animation.hpp" -#include "modes/world.hpp" +#include "modes/linear_world.hpp" #include "physics/physics.hpp" #include "tracks/track.hpp" #include "utils/constants.hpp" @@ -100,7 +100,7 @@ Flyable::Flyable(AbstractKart *kart, PowerupManager::PowerupType type, */ void Flyable::createPhysics(float forw_offset, const Vec3 &velocity, btCollisionShape *shape, - float restitution, const float gravity, + float restitution, const btVector3 gravity, const bool rotates, const bool turn_around, const btTransform* custom_direction) { @@ -132,7 +132,7 @@ void Flyable::createPhysics(float forw_offset, const Vec3 &velocity, m_user_pointer.set(this); World::getWorld()->getPhysics()->addBody(getBody()); - m_body->setGravity(btVector3(0.0f, gravity, 0)); + m_body->setGravity(gravity); // Rotate velocity to point in the right direction btVector3 v=trans.getBasis()*velocity; @@ -290,6 +290,23 @@ void Flyable::getLinearKartItemIntersection (const Vec3 &origin, btTransform trans = target_kart->getTrans(); Vec3 target_direction(trans.getBasis().getColumn(2)); + Vec3 owner_gravity; + if (race_manager->getMinorMode() != RaceManager::MINOR_MODE_3_STRIKES && + race_manager->getMinorMode() != RaceManager::MINOR_MODE_SOCCER) + { + owner_gravity = m_owner->getBody()->getGravity(); + core::matrix4 m; + m.buildRotateFromTo(core::vector3df(0.0f, -1.0f, 0.0f), owner_gravity.toIrrVector()); + + core::vector3df r_t_k_l = relative_target_kart_loc.toIrrVector(); + m.rotateVect(r_t_k_l); + relative_target_kart_loc = r_t_k_l; + + core::vector3df t_d = target_direction.toIrrVector(); + m.rotateVect(t_d); + target_direction = t_d; + } + float dx = relative_target_kart_loc.getX(); float dy = relative_target_kart_loc.getY(); float dz = relative_target_kart_loc.getZ(); @@ -301,6 +318,7 @@ void Flyable::getLinearKartItemIntersection (const Vec3 &origin, * target_kart->getSpeed(); float target_kart_heading = target_kart->getHeading(); + target_kart_heading = atan2f(target_direction.getX(), target_direction.getZ()); float dist = -(target_kart_speed / item_XZ_speed) * (dx * cosf(target_kart_heading) - @@ -378,15 +396,35 @@ bool Flyable::updateAndDelete(float dt) return true; } - // Add the position offset so that the flyable can adjust its position - // (usually to do the raycast from a slightly higher position to avoid - // problems finding the terrain in steep uphill sections). - if(m_do_terrain_info) - TerrainInfo::update(xyz+m_position_offset); + if (m_do_terrain_info) + { + Vec3 towards = getBody()->getGravity(); + towards.normalize(); + // Add the position offset so that the flyable can adjust its position + // (usually to do the raycast from a slightly higher position to avoid + // problems finding the terrain in steep uphill sections). + // towards is a unit vector. so we can multiply -towards to offset the position + // by one unit. + TerrainInfo::update(xyz + m_position_offset*(-towards), towards); + } + // Update gravity + if (race_manager->getMinorMode() != RaceManager::MINOR_MODE_3_STRIKES && + race_manager->getMinorMode() != RaceManager::MINOR_MODE_SOCCER) + { + int sector = QuadGraph::UNKNOWN_SECTOR; + QuadGraph::get()->findRoadSector(getXYZ(), §or); + if (sector != QuadGraph::UNKNOWN_SECTOR) + { + float g = World::getWorld()->getTrack()->getGravity(); + Vec3 gravity = -g*QuadGraph::get()->getQuadOfNode(sector).getNormal(); + getBody()->setGravity(gravity); + } + } + if(m_adjust_up_velocity) { - float hat = xyz.getY()-getHoT(); + float hat = (xyz - getHitPoint()).length(); // Use the Height Above Terrain to set the Z velocity. // HAT is clamped by min/max height. This might be somewhat diff --git a/src/items/flyable.hpp b/src/items/flyable.hpp index b15884f0e..b5a618b64 100644 --- a/src/items/flyable.hpp +++ b/src/items/flyable.hpp @@ -149,7 +149,7 @@ protected: const Vec3 &velocity, btCollisionShape *shape, float restitution, - const float gravity=0.0f, + const btVector3 gravity = btVector3(0.0f, 0.0f,0.0f), const bool rotates=false, const bool turn_around=false, const btTransform* customDirection=NULL); diff --git a/src/items/plunger.cpp b/src/items/plunger.cpp index 1ef5a6104..d101eb06b 100644 --- a/src/items/plunger.cpp +++ b/src/items/plunger.cpp @@ -78,14 +78,14 @@ Plunger::Plunger(AbstractKart *kart) createPhysics(forward_offset, m_initial_velocity, new btCylinderShape(0.5f*m_extend), - 0.5f /* restitution */ , gravity, + 0.5f /* restitution */ , btVector3(.0f,gravity,.0f), /* rotates */false , /*turn around*/false, &trans); } else { createPhysics(forward_offset, btVector3(pitch, 0.0f, plunger_speed), new btCylinderShape(0.5f*m_extend), - 0.5f /* restitution */, gravity, + 0.5f /* restitution */, btVector3(.0f,gravity,.0f), false /* rotates */, m_reverse_mode, &kart_transform); } diff --git a/src/items/rubber_ball.cpp b/src/items/rubber_ball.cpp index cc6f268e8..0279d96fb 100644 --- a/src/items/rubber_ball.cpp +++ b/src/items/rubber_ball.cpp @@ -66,8 +66,8 @@ RubberBall::RubberBall(AbstractKart *kart) float forw_offset = 0.5f*kart->getKartLength() + m_extend.getZ()*0.5f+5.0f; createPhysics(forw_offset, btVector3(0.0f, 0.0f, m_speed*2), - new btSphereShape(0.5f*m_extend.getY()), - -70.0f /*gravity*/, + new btSphereShape(0.5f*m_extend.getY()), -70.0f, + btVector3(.0f,-70.0f,.0f) /*gravity*/, true /*rotates*/); // Do not adjust the up velocity diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 835a1fd6c..a2d65b995 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -547,7 +547,7 @@ void Kart::blockViewWithPlunger() btTransform Kart::getAlignedTransform(const float custom_pitch) { btTransform trans = getTrans(); - + /* float pitch = (custom_pitch == -1 ? getTerrainPitch(getHeading()) : custom_pitch); @@ -555,7 +555,12 @@ btTransform Kart::getAlignedTransform(const float custom_pitch) m.setEulerZYX(pitch, getHeading()+m_skidding->getVisualSkidRotation(), 0.0f); trans.setBasis(m); - + */ + btTransform trans2; + trans2.setIdentity(); + trans2.setRotation(btQuaternion(m_skidding->getVisualSkidRotation(), 0, 0)); + trans *= trans2; + return trans; } // getAlignedTransform From c0c4dab8059286cf1006d2f2d154e3c110760267 Mon Sep 17 00:00:00 2001 From: nixt Date: Sun, 20 Jul 2014 18:24:47 +0530 Subject: [PATCH 066/350] Update rubber ball for non-horizontal tracks --- src/items/rubber_ball.cpp | 42 +++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/items/rubber_ball.cpp b/src/items/rubber_ball.cpp index 0279d96fb..d68a15003 100644 --- a/src/items/rubber_ball.cpp +++ b/src/items/rubber_ball.cpp @@ -346,35 +346,34 @@ bool RubberBall::updateAndDelete(float dt) // the previous location (since TerrainInfo wasn't updated). On // the other hand, we can't update TerrainInfo without having // at least a good estimation of the height. - next_xyz.setY(getHoT() + vertical_offset); + // Update height of terrain (which isn't done as part of // Flyable::update for rubber balls. - TerrainInfo::update(next_xyz); + TerrainInfo::update(next_xyz + getNormal()*vertical_offset, -getNormal()); m_height_timer += dt; float height = updateHeight()+m_extend.getY()*0.5f; - float new_y = getHoT()+height; - + if(UserConfigParams::logFlyable()) - Log::debug("[RubberBall]", "ball %d: %f %f %f height %f new_y %f gethot %f ", - m_id, next_xyz.getX(), next_xyz.getY(), next_xyz.getZ(), height, new_y, getHoT()); + Log::debug("[RubberBall]", "ball %d: %f %f %f height %f gethot %f ", + m_id, next_xyz.getX(), next_xyz.getY(), next_xyz.getZ(), height, getHoT()); // No need to check for terrain height if the ball is low to the ground if(height > 0.5f) { float terrain_height = getMaxTerrainHeight(vertical_offset) - m_extend.getY(); - if(new_y>terrain_height) - new_y = terrain_height; + if(height>terrain_height) + height = terrain_height; } if(UserConfigParams::logFlyable()) - Log::verbose("RubberBall", "newy2 %f gmth %f", new_y, + Log::verbose("RubberBall", "newy2 %f gmth %f", height, getMaxTerrainHeight(vertical_offset)); - next_xyz.setY(new_y); + next_xyz = next_xyz + getNormal()*(height); m_previous_xyz = getXYZ(); - m_previous_height = next_xyz.getY()-getHoT(); + m_previous_height = (getXYZ() - getHitPoint()).length(); setXYZ(next_xyz); if(checkTunneling()) @@ -405,12 +404,12 @@ void RubberBall::moveTowardsTarget(Vec3 *next_xyz, float dt) // If the rubber ball is already close to a target, i.e. aiming // at it directly, stop interpolating, instead fly straight // towards it. - Vec3 diff = m_target->getXYZ()-getXYZ(); + Vec3 diff = m_target->getXYZ() - getXYZ(); // Avoid potential division by zero if(diff.length2()==0) *next_xyz = getXYZ(); else - *next_xyz = getXYZ() + (dt*m_speed/diff.length())*diff; + *next_xyz = getXYZ() + (dt*m_speed / diff.length())*diff; Vec3 old_vec = getXYZ()-m_previous_xyz; Vec3 new_vec = *next_xyz - getXYZ(); @@ -421,7 +420,7 @@ void RubberBall::moveTowardsTarget(Vec3 *next_xyz, float dt) angle += 2*M_PI; else if(angle > M_PI) angle -= 2*M_PI; - + // If the angle is too large, adjust next xyz if(fabsf(angle)>m_st_target_max_angle*dt) { @@ -434,6 +433,8 @@ void RubberBall::moveTowardsTarget(Vec3 *next_xyz, float dt) next_xyz->setX(getXYZ().getX() + old_2d.X*dt*m_speed); next_xyz->setZ(getXYZ().getZ() + old_2d.Y*dt*m_speed); } // if fabsf(angle) > m_st_target_angle_max*dt + + *next_xyz -= getNormal()*m_previous_height; assert(!isnan((*next_xyz)[0])); assert(!isnan((*next_xyz)[1])); @@ -594,19 +595,18 @@ float RubberBall::updateHeight() * happen that the raycast down find the track since it uses the * vertical offset, while the raycast up would hit under the track * if the vertical offset is not used). - * \returns The height (Y coordinate) of the next terrain element found by - * a raycast up. If no terrain is found, it returns 99990 + * \returns The distance to the terrain element found by raycast in the up + direction. If no terrain found, it returns 99990 */ float RubberBall::getMaxTerrainHeight(const Vec3 &vertical_offset) const { const TriangleMesh &tm = World::getWorld()->getTrack()->getTriangleMesh(); - Vec3 to(getXYZ()); - to.setY(10000.0f); - Vec3 hit_point; + Vec3 to(getXYZ() + 10000.0f*getNormal()); + Vec3 hit_point; const Material *material; tm.castRay(getXYZ()+vertical_offset, to, &hit_point, &material); - return (material) ? hit_point.getY() : 99999.f; + return (material) ? (hit_point - getXYZ()).length() : 99999.f; } // getMaxTerrainHeight // ---------------------------------------------------------------------------- @@ -633,7 +633,7 @@ void RubberBall::updateDistanceToTarget() m_target->getXYZ().getZ(),m_distance_to_target ); - float height_diff = fabsf(m_target->getXYZ().getY() - getXYZ().getY()); + float height_diff = fabsf((m_target->getXYZ() - getXYZ()).dot(getNormal().normalized())); if(m_distance_to_target < m_st_fast_ping_distance && height_diff < m_st_max_height_difference) From 97c628facac70189a20ce41392d2e16e99c27cae Mon Sep 17 00:00:00 2001 From: nixt Date: Sun, 20 Jul 2014 18:27:51 +0530 Subject: [PATCH 067/350] New implementation for the cake throwing code that finds the angle velocity of the cake to hit a target in an arbitrary orientation. --- src/items/cake.cpp | 11 +++- src/items/flyable.cpp | 125 ++++++++++++++++++------------------------ 2 files changed, 60 insertions(+), 76 deletions(-) diff --git a/src/items/cake.cpp b/src/items/cake.cpp index 51d48c1f2..785c0d8b1 100644 --- a/src/items/cake.cpp +++ b/src/items/cake.cpp @@ -35,11 +35,14 @@ Cake::Cake (AbstractKart *kart) : Flyable(kart, PowerupManager::POWERUP_CAKE) { m_target = NULL; + setDoTerrainInfo(false); + Vec3 gravity_vector; if (race_manager->getMinorMode() != RaceManager::MINOR_MODE_3_STRIKES && race_manager->getMinorMode() != RaceManager::MINOR_MODE_SOCCER) { - gravity_vector = kart->getBody()->getGravity(); + btQuaternion q = kart->getTrans().getRotation(); + gravity_vector = Vec3(0, -1, 0).rotate(q.getAxis(), q.getAngle()); gravity_vector = gravity_vector.normalize() * m_gravity; } else @@ -94,8 +97,10 @@ Cake::Cake (AbstractKart *kart) : Flyable(kart, PowerupManager::POWERUP_CAKE) &fire_angle, &up_velocity); // apply transformation to the bullet object (without pitch) - trans.setRotation(btQuaternion(btVector3(0,1,0), fire_angle)); - + btQuaternion q; + q = trans.getRotation() * btQuaternion(btVector3(0, 1, 0), fire_angle); + trans.setRotation(q); + m_initial_velocity = Vec3(0.0f, up_velocity, m_speed); createPhysics(forward_offset, m_initial_velocity, diff --git a/src/items/flyable.cpp b/src/items/flyable.cpp index d3b4f2100..24efc3784 100644 --- a/src/items/flyable.cpp +++ b/src/items/flyable.cpp @@ -285,76 +285,61 @@ void Flyable::getLinearKartItemIntersection (const Vec3 &origin, float *fire_angle, float *up_velocity) { - Vec3 relative_target_kart_loc = target_kart->getXYZ() - origin; - + // Transform the target into the firing kart's frame of reference + btTransform inv_trans = m_owner->getTrans().inverse(); + + Vec3 relative_target_kart_loc = inv_trans(target_kart->getXYZ()); + + // Find the direction target is moving in btTransform trans = target_kart->getTrans(); Vec3 target_direction(trans.getBasis().getColumn(2)); - Vec3 owner_gravity; - if (race_manager->getMinorMode() != RaceManager::MINOR_MODE_3_STRIKES && - race_manager->getMinorMode() != RaceManager::MINOR_MODE_SOCCER) - { - owner_gravity = m_owner->getBody()->getGravity(); - core::matrix4 m; - m.buildRotateFromTo(core::vector3df(0.0f, -1.0f, 0.0f), owner_gravity.toIrrVector()); + // Now rotate it to the firing kart's frame of reference + btQuaternion inv_rotate = inv_trans.getRotation(); + target_direction = + target_direction.rotate(inv_rotate.getAxis(), inv_rotate.getAngle()); + + // Now we try to find the angle to aim at to hit the target. + // Warning : Funky stuff going on below. To understand, see answer by + // Jeffrey Hantin here : + // http://stackoverflow.com/questions/2248876/2d-game-fire-at-a-moving-target-by-predicting-intersection-of-projectile-and-u + + float target_x_speed = target_direction.getX()*target_kart->getSpeed(); + float target_z_speed = target_direction.getZ()*target_kart->getSpeed(); + float target_y_speed = target_direction.getY()*target_kart->getSpeed(); - core::vector3df r_t_k_l = relative_target_kart_loc.toIrrVector(); - m.rotateVect(r_t_k_l); - relative_target_kart_loc = r_t_k_l; - - core::vector3df t_d = target_direction.toIrrVector(); - m.rotateVect(t_d); - target_direction = t_d; - } - - float dx = relative_target_kart_loc.getX(); - float dy = relative_target_kart_loc.getY(); - float dz = relative_target_kart_loc.getZ(); - - float gy = target_direction.getY(); - - //Projected onto X-Z plane - float target_kart_speed = target_direction.length_2d() - * target_kart->getSpeed(); - - float target_kart_heading = target_kart->getHeading(); - target_kart_heading = atan2f(target_direction.getX(), target_direction.getZ()); - - float dist = -(target_kart_speed / item_XZ_speed) - * (dx * cosf(target_kart_heading) - - dz * sinf(target_kart_heading) ); - - float fire_th = (dx*dist - dz * sqrtf(dx*dx + dz*dz - dist*dist)) - / (dx*dx + dz*dz); - if(fire_th>1) - fire_th = 1.0f; - else if (fire_th<-1.0f) - fire_th = -1.0f; - fire_th = (((dist - dx*fire_th) / dz > 0) ? -acosf(fire_th) - : acosf(fire_th)); - - float time = 0.0f; - float a = item_XZ_speed * sinf (fire_th) - + target_kart_speed * sinf (target_kart_heading); - float b = item_XZ_speed * cosf (fire_th) - + target_kart_speed * cosf (target_kart_heading); - - if (fabsf(a) > fabsf(b)) time = fabsf (dx / a); - else if (b != 0.0f) time = fabsf(dz / b); - - if (fire_th > M_PI) - fire_th -= M_PI; - else - fire_th += M_PI; + float a = (target_x_speed*target_x_speed) + (target_z_speed*target_z_speed) - + (item_XZ_speed*item_XZ_speed); + float b = 2 * (target_x_speed * (relative_target_kart_loc.getX()) + + target_z_speed * (relative_target_kart_loc.getZ())); + float c = relative_target_kart_loc.getX()*relative_target_kart_loc.getX() + + relative_target_kart_loc.getZ()*relative_target_kart_loc.getZ(); + + float discriminant = b*b - 4 * a*c; + if (discriminant < 0) discriminant = 0; + float t1 = (-b + sqrt(discriminant)) / (2 * a); + float t2 = (-b - sqrt(discriminant)) / (2 * a); + float time; + if (t1 >= 0 && t1getSpeed()); + float angle = atan2f(aimX, aimZ); + + *fire_angle = angle; + + // Now find the up_velocity. This is an application of newton's equation. + *up_velocity = (0.5f * time * gravity) + (relative_target_kart_loc.getY() / time) + + ( target_y_speed); } // getLinearKartItemIntersection //----------------------------------------------------------------------------- @@ -406,21 +391,15 @@ bool Flyable::updateAndDelete(float dt) // towards is a unit vector. so we can multiply -towards to offset the position // by one unit. TerrainInfo::update(xyz + m_position_offset*(-towards), towards); - } - - // Update gravity - if (race_manager->getMinorMode() != RaceManager::MINOR_MODE_3_STRIKES && - race_manager->getMinorMode() != RaceManager::MINOR_MODE_SOCCER) - { - int sector = QuadGraph::UNKNOWN_SECTOR; - QuadGraph::get()->findRoadSector(getXYZ(), §or); - if (sector != QuadGraph::UNKNOWN_SECTOR) + if (race_manager->getMinorMode() != RaceManager::MINOR_MODE_3_STRIKES && + race_manager->getMinorMode() != RaceManager::MINOR_MODE_SOCCER) { + Vec3 normal = TerrainInfo::getNormal(); float g = World::getWorld()->getTrack()->getGravity(); - Vec3 gravity = -g*QuadGraph::get()->getQuadOfNode(sector).getNormal(); - getBody()->setGravity(gravity); + getBody()->setGravity(-g*normal); } } + if(m_adjust_up_velocity) { From 6e2d92ec20a99dd4440f1c11cf60f37a5d689f79 Mon Sep 17 00:00:00 2001 From: nixt Date: Sun, 20 Jul 2014 18:29:59 +0530 Subject: [PATCH 068/350] Remove old comment --- src/items/rubber_ball.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/items/rubber_ball.cpp b/src/items/rubber_ball.cpp index d68a15003..db3b53f15 100644 --- a/src/items/rubber_ball.cpp +++ b/src/items/rubber_ball.cpp @@ -342,11 +342,7 @@ bool RubberBall::updateAndDelete(float dt) bool close_to_ground = 2.0*m_previous_height < m_current_max_height; float vertical_offset = close_to_ground ? 4.0f : 2.0f; - // Note that at this stage getHoT still reports the height at - // the previous location (since TerrainInfo wasn't updated). On - // the other hand, we can't update TerrainInfo without having - // at least a good estimation of the height. - + // Update height of terrain (which isn't done as part of // Flyable::update for rubber balls. TerrainInfo::update(next_xyz + getNormal()*vertical_offset, -getNormal()); From 413c062932bbf8a74e84bb232184b35e8cf2d973 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 21 Jul 2014 16:24:44 +1000 Subject: [PATCH 069/350] Simplify code by using the kart's normal. --- src/karts/abstract_kart.hpp | 4 ++++ src/karts/kart.hpp | 6 +++++- src/karts/rescue_animation.cpp | 4 +--- src/modes/world.cpp | 4 +--- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/karts/abstract_kart.hpp b/src/karts/abstract_kart.hpp index af67b278b..4b18af2ea 100644 --- a/src/karts/abstract_kart.hpp +++ b/src/karts/abstract_kart.hpp @@ -390,6 +390,10 @@ public: // ------------------------------------------------------------------------ virtual void crashed(const Material *m, const Vec3 &normal) = 0; // ------------------------------------------------------------------------ + /** Returns the normal of the terrain the kart is over atm. This is + * defined even if the kart is flying. */ + virtual const Vec3& getNormal() const = 0; + // ------------------------------------------------------------------------ /** Returns the height of the terrain. we're currently above */ virtual float getHoT() const = 0; // ------------------------------------------------------------------------ diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index 8ce5ea519..3b46c78f3 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -374,7 +374,7 @@ public: // ------------------------------------------------------------------------ /** Returns true if the kart is close to the ground, used to dis/enable * the upright constraint to allow for more realistic explosions. */ - bool isNearGround () const; + bool isNearGround() const; // ------------------------------------------------------------------------ /** Returns true if the kart is eliminated. */ virtual bool isEliminated() const { return m_eliminated; } @@ -419,6 +419,10 @@ public: // ------------------------------------------------------------------------ virtual void setOnScreenText(const wchar_t *text); // ------------------------------------------------------------------------ + /** Returns the normal of the terrain the kart is over atm. This is + * defined even if the kart is flying. */ + virtual const Vec3& getNormal() const {return m_terrain_info->getNormal();} + // ------------------------------------------------------------------------ /** For debugging only: check if a kart is flying. */ bool isFlying() const { return m_flying; } }; // Kart diff --git a/src/karts/rescue_animation.cpp b/src/karts/rescue_animation.cpp index 3a2d7d50f..0cb0a6607 100644 --- a/src/karts/rescue_animation.cpp +++ b/src/karts/rescue_animation.cpp @@ -112,10 +112,8 @@ RescueAnimation::~RescueAnimation() */ void RescueAnimation::update(float dt) { - - //m_xyz.setY(m_xyz.getY() + dt*m_velocity); btQuaternion q1 = m_kart->getTrans().getRotation(); - m_xyz = m_xyz + dt*m_velocity*(Vec3(0, 1, 0).rotate(q1.getAxis(), q1.getAngle())); + m_xyz += dt*m_velocity * m_kart->getNormal(); m_kart->setXYZ(m_xyz); m_curr_rotation += dt*m_add_rotation; btQuaternion q(m_curr_rotation.getHeading(), m_curr_rotation.getPitch(), diff --git a/src/modes/world.cpp b/src/modes/world.cpp index f242b8702..42dd4607e 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -715,9 +715,7 @@ void World::moveKartTo(AbstractKart* kart, const btTransform &transform) btQuaternion rot = pos.getRotation(); // Move the kart - Vec3 xyz = pos.getOrigin() + btVector3(0, 0.5f*kart->getKartHeight(),0.0f). - rotate(rot.getAxis(),rot.getAngle()); - + Vec3 xyz = pos.getOrigin() + 0.5f*kart->getKartHeight() * kart->getNormal(); pos.setOrigin(xyz); kart->setXYZ(xyz); kart->setRotation(pos.getRotation()); From 8a4911d41dad0a01eb465fc3b71548cbdbe7f829 Mon Sep 17 00:00:00 2001 From: nixt Date: Tue, 22 Jul 2014 16:36:08 +0530 Subject: [PATCH 070/350] Rubber ball (Basketball) now fixed for non-horizontal tracks. --- src/items/rubber_ball.cpp | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/items/rubber_ball.cpp b/src/items/rubber_ball.cpp index db3b53f15..65d244446 100644 --- a/src/items/rubber_ball.cpp +++ b/src/items/rubber_ball.cpp @@ -401,23 +401,29 @@ void RubberBall::moveTowardsTarget(Vec3 *next_xyz, float dt) // at it directly, stop interpolating, instead fly straight // towards it. Vec3 diff = m_target->getXYZ() - getXYZ(); + diff = diff - diff.dot(getNormal())*getNormal(); // Avoid potential division by zero if(diff.length2()==0) - *next_xyz = getXYZ(); + *next_xyz = getXYZ() - getNormal()*m_previous_height; else - *next_xyz = getXYZ() + (dt*m_speed / diff.length())*diff; + *next_xyz = getXYZ() - getNormal()*m_previous_height +(dt*m_speed / diff.length())*diff; Vec3 old_vec = getXYZ()-m_previous_xyz; Vec3 new_vec = *next_xyz - getXYZ(); - float angle = atan2(new_vec.getZ(), new_vec.getX()) - - atan2(old_vec.getZ(), old_vec.getX()); + //float angle = atan2(new_vec.getZ(), new_vec.getX()) + // - atan2(old_vec.getZ(), old_vec.getX()); + float angle = new_vec.angle(old_vec); + // Adjust angle to be between -180 and 180 degrees if(angle < -M_PI) angle += 2*M_PI; else if(angle > M_PI) angle -= 2*M_PI; - + + if (diff.length() < m_target->getKartLength()) + hit((AbstractKart*)m_target); // If the angle is too large, adjust next xyz + /* if(fabsf(angle)>m_st_target_max_angle*dt) { core::vector2df old_2d(old_vec.getX(), old_vec.getZ()); @@ -429,9 +435,8 @@ void RubberBall::moveTowardsTarget(Vec3 *next_xyz, float dt) next_xyz->setX(getXYZ().getX() + old_2d.X*dt*m_speed); next_xyz->setZ(getXYZ().getZ() + old_2d.Y*dt*m_speed); } // if fabsf(angle) > m_st_target_angle_max*dt - - *next_xyz -= getNormal()*m_previous_height; - + */ + //*next_xyz -= getNormal()*m_previous_height; assert(!isnan((*next_xyz)[0])); assert(!isnan((*next_xyz)[1])); assert(!isnan((*next_xyz)[2])); From 20603a208448118de46c19085537ca06aaba1cf2 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 23 Jul 2014 07:59:47 +1000 Subject: [PATCH 071/350] Try better computation of heading (which might be better if the kart is not in the xz plane). --- src/karts/moveable.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/karts/moveable.cpp b/src/karts/moveable.cpp index 84f5d17e0..f85474a69 100644 --- a/src/karts/moveable.cpp +++ b/src/karts/moveable.cpp @@ -129,8 +129,8 @@ void Moveable::update(float dt) if(m_body->getInvMass()!=0) m_motion_state->getWorldTransform(m_transform); m_velocityLC = getVelocity()*m_transform.getBasis(); - Vec3 forw_vec = m_transform.getBasis().getColumn(0); - m_heading = -atan2f(forw_vec.getZ(), forw_vec.getX()); + Vec3 forw_vec = m_transform.getBasis().getColumn(2); + m_heading = atan2f(forw_vec.getX(), forw_vec.getZ()); // The pitch in hpr is in between -pi and pi. But for the camera it // must be restricted to -pi/2 and pi/2 - so recompute it by restricting From 6c02849df3d00abbc87c409570d154e95cb23652 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 23 Jul 2014 08:02:13 +1000 Subject: [PATCH 072/350] Avoid use of quaternions, simplify rescue animation. Heading is currently unchanged, but it looks ok-ish. --- src/karts/moveable.hpp | 18 +++++++++++----- src/karts/rescue_animation.cpp | 39 ++++++++++++++-------------------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/karts/moveable.hpp b/src/karts/moveable.hpp index 42d82217a..2a4a71498 100644 --- a/src/karts/moveable.hpp +++ b/src/karts/moveable.hpp @@ -90,15 +90,23 @@ public: m_transform.setOrigin(a); if(m_motion_state) m_motion_state->setWorldTransform(m_transform); - } + } // setXYZ // ------------------------------------------------------------------------ - /** Sets the rotation of this moveable. */ - void setRotation(const btQuaternion&a) + /** Sets the rotation of the physical body this moveable. */ + void setRotation(const btMatrix3x3 &m) { - m_transform.setRotation(a); + m_transform.setBasis(m); if(m_motion_state) m_motion_state->setWorldTransform(m_transform); - } + } // setRotation(btMatrix3x3) + // ------------------------------------------------------------------------ + /** Sets the rotation of the physical body this moveable. */ + void setRotation(const btQuaternion &q) + { + m_transform.setRotation(q); + if(m_motion_state) + m_motion_state->setWorldTransform(m_transform); + } // setRotation(btQuaternion) // ------------------------------------------------------------------------ virtual void updateGraphics(float dt, const Vec3& off_xyz, const btQuaternion& off_rotation); diff --git a/src/karts/rescue_animation.cpp b/src/karts/rescue_animation.cpp index 0cb0a6607..e6db0a189 100644 --- a/src/karts/rescue_animation.cpp +++ b/src/karts/rescue_animation.cpp @@ -45,27 +45,20 @@ RescueAnimation::RescueAnimation(AbstractKart *kart, bool is_auto_rescue) m_kart->getAttachment()->clear(); - // We get the final transform of the kart so that it is rotated - // accordingly during the rescue animation - World * world = World::getWorld(); - unsigned int index = world->getRescuePositionIndex(m_kart); - btTransform t = world->getRescueTransform(index); - Vec3 up = t.getBasis().getColumn(1); - float target_pitch = atan2(up.getZ(), fabsf(up.getY())); - float target_roll = atan2(up.getX(), up.getY()); + // Get the current rotation of the kart + m_curr_rotation = m_kart->getNode()->getRotation() * DEGREE_TO_RAD; - - m_curr_rotation.setPitch(m_kart->getPitch()); - m_curr_rotation.setRoll(m_kart->getRoll()); - m_curr_rotation.setHeading(0); - - Vec3 required_rotation; - required_rotation.setPitch(target_pitch - m_kart->getPitch()); - required_rotation.setRoll(target_roll - m_kart->getRoll()); - required_rotation.setHeading(0); - m_add_rotation = -required_rotation/m_timer; - - m_curr_rotation.setHeading(m_kart->getHeading()); + // Determine the rotation that will rotate the kart from the current + // up direction to the right up direction it should have according to + // the normal at the kart's location + Vec3 up = m_kart->getTrans().getBasis().getColumn(1); + btQuaternion q = shortestArcQuat(up, m_kart->getNormal()); + + // Store this rotation as 'delta HPR', which is added over time to the + // current rotation to end up (after m_timer seconds) with the right up + // rotation + m_add_rotation.setHPR(q); + m_add_rotation /= m_timer; // Add a hit unless it was auto-rescue if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES && @@ -112,13 +105,13 @@ RescueAnimation::~RescueAnimation() */ void RescueAnimation::update(float dt) { - btQuaternion q1 = m_kart->getTrans().getRotation(); m_xyz += dt*m_velocity * m_kart->getNormal(); m_kart->setXYZ(m_xyz); m_curr_rotation += dt*m_add_rotation; - btQuaternion q(m_curr_rotation.getHeading(), m_curr_rotation.getPitch(), + btMatrix3x3 m; + m.setEulerZYX(m_curr_rotation.getPitch(), m_curr_rotation.getHeading(), m_curr_rotation.getRoll()); - m_kart->setRotation(q); + m_kart->setRotation(m); AbstractKartAnimation::update(dt); From f0ea8a1a38ec51459eab16fb4048e79beca226b7 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 23 Jul 2014 08:03:10 +1000 Subject: [PATCH 073/350] Simplify up-offset computation. --- src/tracks/track.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index a3ab67ae0..20cbfae32 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -2453,7 +2453,7 @@ bool Track::findGround(AbstractKart *kart) // - so I'll leave it in for now. float offset = kart->getKartProperties()->getSuspensionRest() + kart->getKartProperties()->getWheelRadius(); - t.setOrigin(hit_point+ (btVector3(0, offset, 0).rotate(q.getAxis(),q.getAngle()) ) ); + t.setOrigin(hit_point+ quadNormal * offset); kart->getBody()->setCenterOfMassTransform(t); kart->setTrans(t); From 0ef42111bca7040a6d9b9c759b4740b057735ed2 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 23 Jul 2014 08:11:30 +1000 Subject: [PATCH 074/350] Fixed rescue transform: the heading rotation need to be done around (0,1,0), not around the normal, and the order of rotation needs to be first the heading rotation, then pitch/roll to make the kart parallel to the ground. --- src/modes/linear_world.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index 2b632ef3c..cec93d0f3 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -636,6 +636,7 @@ btTransform LinearWorld::getRescueTransform(unsigned int index) const const Vec3 &xyz = QuadGraph::get()->getQuadOfNode(index).getCenter(); const Vec3 &normal = QuadGraph::get()->getQuadOfNode(index).getNormal(); btTransform pos; + pos.setOrigin(xyz); // First rotate into the quad's plane (q1), then rotate so that the kart points in the // right direction (q2). @@ -646,9 +647,18 @@ btTransform LinearWorld::getRescueTransform(unsigned int index) const } else q1 = btQuaternion(Vec3(0,1,0),0); - btQuaternion q2(btVector3(normal), m_track->getAngle(index)); - pos.setOrigin(xyz); - pos.setRotation(q2*q1); + btQuaternion q2(btVector3(0,1,0), m_track->getAngle(index)); + + // First apply the heading change, than the 'parallelisation' to the plane + pos.setRotation(q1*q2); +#ifdef DEBUG + btQuaternion r = q1*q2; + btVector3 axis = r.getAxis(); + float angle = r.getAngle(); + Log::verbose("rescue", "%d angle %f axis %f %f %f pos %f %f %f", + index, angle, axis.getX(),axis.getY(),axis.getZ(), + xyz.getX(),xyz.getY(),xyz.getZ()); +#endif return pos; } // getRescueTransform From f19afc98cffb58a35ec4355a8a976b299b9f88bf Mon Sep 17 00:00:00 2001 From: nixt Date: Wed, 23 Jul 2014 19:59:20 +0530 Subject: [PATCH 075/350] Fix plunger to work on vertical tracks. --- src/items/plunger.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/items/plunger.cpp b/src/items/plunger.cpp index d101eb06b..bfed222e7 100644 --- a/src/items/plunger.cpp +++ b/src/items/plunger.cpp @@ -40,6 +40,8 @@ Plunger::Plunger(AbstractKart *kart) { const float gravity = 0.0f; + setDoTerrainInfo(false); + float forward_offset = 0.5f*kart->getKartLength()+0.5f*m_extend.getZ(); float up_velocity = 0.0f; float plunger_speed = 2 * m_speed; @@ -71,8 +73,9 @@ Plunger::Plunger(AbstractKart *kart) &fire_angle, &up_velocity); btTransform trans = kart->getTrans(); - - trans.setRotation(btQuaternion(btVector3(0, 1, 0), fire_angle)); + btQuaternion q; + q = trans.getRotation()*(btQuaternion(btVector3(0, 1, 0), fire_angle)); + trans.setRotation(q); m_initial_velocity = btVector3(0.0f, up_velocity, plunger_speed); From 2673e598449886069edeb10157e5ae690ddc64b5 Mon Sep 17 00:00:00 2001 From: nixt Date: Wed, 23 Jul 2014 20:00:15 +0530 Subject: [PATCH 076/350] Fix a bug where the rubber ball would collide with the ceiling in a tunnel. --- src/items/flyable.cpp | 2 +- src/items/rubber_ball.cpp | 45 +++++++++++++++++---------------------- src/items/rubber_ball.hpp | 3 ++- 3 files changed, 23 insertions(+), 27 deletions(-) diff --git a/src/items/flyable.cpp b/src/items/flyable.cpp index 24efc3784..5651180be 100644 --- a/src/items/flyable.cpp +++ b/src/items/flyable.cpp @@ -300,7 +300,7 @@ void Flyable::getLinearKartItemIntersection (const Vec3 &origin, target_direction.rotate(inv_rotate.getAxis(), inv_rotate.getAngle()); // Now we try to find the angle to aim at to hit the target. - // Warning : Funky stuff going on below. To understand, see answer by + // Warning : Funky math stuff going on below. To understand, see answer by // Jeffrey Hantin here : // http://stackoverflow.com/questions/2248876/2d-game-fire-at-a-moving-target-by-predicting-intersection-of-projectile-and-u diff --git a/src/items/rubber_ball.cpp b/src/items/rubber_ball.cpp index 65d244446..4a4d5e0ba 100644 --- a/src/items/rubber_ball.cpp +++ b/src/items/rubber_ball.cpp @@ -357,15 +357,23 @@ bool RubberBall::updateAndDelete(float dt) // No need to check for terrain height if the ball is low to the ground if(height > 0.5f) { - float terrain_height = getMaxTerrainHeight(vertical_offset) + float tunnel_height = getTunnelHeight(next_xyz, vertical_offset) - m_extend.getY(); - if(height>terrain_height) - height = terrain_height; + // If the current height of ball (above terrain) is higher than the + // tunnel height then set adjust max height and compute new height again. + // Else reset the max height. + if (height > tunnel_height) + { + m_max_height = tunnel_height; + height = updateHeight(); + } + else + m_max_height = m_st_max_height[m_type]; } if(UserConfigParams::logFlyable()) Log::verbose("RubberBall", "newy2 %f gmth %f", height, - getMaxTerrainHeight(vertical_offset)); + getTunnelHeight(next_xyz,vertical_offset)); next_xyz = next_xyz + getNormal()*(height); m_previous_xyz = getXYZ(); @@ -419,24 +427,10 @@ void RubberBall::moveTowardsTarget(Vec3 *next_xyz, float dt) angle += 2*M_PI; else if(angle > M_PI) angle -= 2*M_PI; - + // If ball is close to the target, then explode if (diff.length() < m_target->getKartLength()) hit((AbstractKart*)m_target); - // If the angle is too large, adjust next xyz - /* - if(fabsf(angle)>m_st_target_max_angle*dt) - { - core::vector2df old_2d(old_vec.getX(), old_vec.getZ()); - if(old_2d.getLengthSQ()==0.0f) old_2d.Y = 1.0f; - old_2d.normalize(); - old_2d.rotateBy( RAD_TO_DEGREE * dt - * (angle > 0 ? m_st_target_max_angle - : -m_st_target_max_angle)); - next_xyz->setX(getXYZ().getX() + old_2d.X*dt*m_speed); - next_xyz->setZ(getXYZ().getZ() + old_2d.Y*dt*m_speed); - } // if fabsf(angle) > m_st_target_angle_max*dt - */ - //*next_xyz -= getNormal()*m_previous_height; + assert(!isnan((*next_xyz)[0])); assert(!isnan((*next_xyz)[1])); assert(!isnan((*next_xyz)[2])); @@ -599,15 +593,16 @@ float RubberBall::updateHeight() * \returns The distance to the terrain element found by raycast in the up direction. If no terrain found, it returns 99990 */ -float RubberBall::getMaxTerrainHeight(const Vec3 &vertical_offset) const +float RubberBall::getTunnelHeight(const Vec3 &next_xyz, const float vertical_offset) const { const TriangleMesh &tm = World::getWorld()->getTrack()->getTriangleMesh(); - Vec3 to(getXYZ() + 10000.0f*getNormal()); - Vec3 hit_point; + Vec3 from(next_xyz + vertical_offset*getNormal()); + Vec3 to(next_xyz + 10000.0f*getNormal()); + Vec3 hit_point; const Material *material; - tm.castRay(getXYZ()+vertical_offset, to, &hit_point, &material); + tm.castRay(from, to, &hit_point, &material); - return (material) ? (hit_point - getXYZ()).length() : 99999.f; + return (material) ? (hit_point - next_xyz).length() : 99999.f; } // getMaxTerrainHeight // ---------------------------------------------------------------------------- diff --git a/src/items/rubber_ball.hpp b/src/items/rubber_ball.hpp index a84d0348b..3287d4ea0 100644 --- a/src/items/rubber_ball.hpp +++ b/src/items/rubber_ball.hpp @@ -196,7 +196,8 @@ private: void interpolate(Vec3 *next_xyz, float dt); void moveTowardsTarget(Vec3 *next_xyz, float dt); void initializeControlPoints(const Vec3 &xyz); - float getMaxTerrainHeight(const Vec3 &vertical_offset) const; + float getTunnelHeight(const Vec3 &next_xyz, + const float vertical_offset) const; bool checkTunneling(); public: RubberBall (AbstractKart* kart); From 78cdc3bd693e856e77b8a00f847a9cd1b798e396 Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 24 Jul 2014 21:38:58 +1000 Subject: [PATCH 077/350] Fixed rescue bug: the kart orientation adjustment would cause a collision with the track on rescue. For now disable the orientation if the kart is close to the ground. A proper solution will be implemented as part of the soft-landing branch. --- src/karts/kart.cpp | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index a2d65b995..c33677486 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -2037,23 +2037,25 @@ void Kart::updatePhysics(float dt) // that the kart rotates in mid-air and lands on its side. if(m_vehicle->getNumWheelsOnGround()==0) { - btVector3 kart_up = getTrans().getBasis().getColumn(1); // up vector - btVector3 terrain_up = m_body->getGravity(); - float g = World::getWorld()->getTrack()->getGravity(); - // Normalize the gravity, g is the length of the vector - btVector3 new_up = 0.9f * kart_up + 0.1f * terrain_up/-g; - // Get the rotation (hpr) based on current heading. - Vec3 rotation(getHeading(), new_up); - btMatrix3x3 m; - m.setEulerZYX(rotation.getX(), rotation.getY(), rotation.getZ()); - // We can't use getXYZ() for the position here, since the position is - // based on interpolation, while the actual center-of-mass-transform - // is based on the actual value every 1/60 of a second (using getXYZ() - // would result in the kart being pushed ahead a bit, making it jump - // much further, depending on fps) - btTransform new_trans(m, m_body->getCenterOfMassTransform().getOrigin()); - //setTrans(new_trans); - m_body->setCenterOfMassTransform(new_trans); + Vec3 diff = getXYZ() - getTerrainInfo()->getHitPoint(); + float height = diff.dot(getNormal()); + if(height>0.5f) + { + btVector3 kart_up = getTrans().getBasis().getColumn(1); // up vector + btVector3 new_up = 0.9f * kart_up + 0.1f * getNormal(); + // Get the rotation (hpr) based on current heading. + Vec3 rotation(getHeading(), new_up); + btMatrix3x3 m; + m.setEulerZYX(rotation.getX(), rotation.getY(), rotation.getZ()); + // We can't use getXYZ() for the position here, since the position is + // based on interpolation, while the actual center-of-mass-transform + // is based on the actual value every 1/60 of a second (using getXYZ() + // would result in the kart being pushed ahead a bit, making it jump + // much further, depending on fps) + btTransform new_trans(m, m_body->getCenterOfMassTransform().getOrigin()); + //setTrans(new_trans); + m_body->setCenterOfMassTransform(new_trans); + } } // To avoid tunneling (which can happen on long falls), clamp the From 051ee241731db03fe98fe0a9363f310490e07683 Mon Sep 17 00:00:00 2001 From: nixt Date: Fri, 25 Jul 2014 15:11:49 +0530 Subject: [PATCH 078/350] Fix item orientation bug. Items should now appear correctly. --- src/items/item.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/items/item.cpp b/src/items/item.cpp index a2af7dd2c..7d42c99df 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -40,7 +40,9 @@ Item::Item(ItemType type, const Vec3& xyz, const Vec3& normal, m_distance_2 = 1.2f; initItem(type, xyz); // Sets heading to 0, and sets pitch and roll depending on the normal. */ - m_original_hpr = Vec3(0, normal); + m_original_hpr.setHPR(btQuaternion(-normal.cross(Vec3(0, 1, 0)), + normal.angle(Vec3(0, 1, 0))) + ); m_original_mesh = mesh; m_original_lowmesh = lowres_mesh; m_listener = NULL; From 4cc406062f36147d1e6377ac9b376dd057def3bb Mon Sep 17 00:00:00 2001 From: nixt Date: Fri, 25 Jul 2014 20:31:42 +0530 Subject: [PATCH 079/350] Fix the initial raycast to make it go towards the track. --- src/items/rubber_ball.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/items/rubber_ball.cpp b/src/items/rubber_ball.cpp index 4a4d5e0ba..42a1ef467 100644 --- a/src/items/rubber_ball.cpp +++ b/src/items/rubber_ball.cpp @@ -67,7 +67,7 @@ RubberBall::RubberBall(AbstractKart *kart) createPhysics(forw_offset, btVector3(0.0f, 0.0f, m_speed*2), new btSphereShape(0.5f*m_extend.getY()), -70.0f, - btVector3(.0f,-70.0f,.0f) /*gravity*/, + btVector3(.0f,.0f,.0f) /*gravity*/, true /*rotates*/); // Do not adjust the up velocity @@ -97,7 +97,8 @@ RubberBall::RubberBall(AbstractKart *kart) // initialises the current graph node TrackSector::update(getXYZ()); - TerrainInfo::update(getXYZ()); + Vec3 normal = QuadGraph::get()->getQuadOfNode(getCurrentGraphNode()).getNormal(); + TerrainInfo::update(getXYZ(), -normal); initializeControlPoints(m_owner->getXYZ()); } // RubberBall @@ -302,7 +303,7 @@ void RubberBall::init(const XMLNode &node, scene::IMesh *rubberball) */ bool RubberBall::updateAndDelete(float dt) { - LinearWorld *world = dynamic_cast(World::getWorld()); + LinearWorld *world = dynamic_cast(World::getWorld()); // FIXME: what does the rubber ball do in case of battle mode?? if(!world) return true; From 52f773f19b7c688ee30c88e930f6350c643c1ab3 Mon Sep 17 00:00:00 2001 From: nixt Date: Tue, 29 Jul 2014 00:36:36 +0530 Subject: [PATCH 080/350] Disable debug messages. --- src/karts/rescue_animation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/karts/rescue_animation.cpp b/src/karts/rescue_animation.cpp index e6db0a189..90811cded 100644 --- a/src/karts/rescue_animation.cpp +++ b/src/karts/rescue_animation.cpp @@ -38,7 +38,7 @@ RescueAnimation::RescueAnimation(AbstractKart *kart, bool is_auto_rescue) : AbstractKartAnimation(kart, "RescueAnimation") { m_referee = new Referee(*m_kart); - m_kart->getNode()->addChild(m_referee->getSceneNode()); + m_kart->getNode()->addChild(m_referee->getSceneNode()); m_timer = m_kart->getKartProperties()->getRescueTime(); m_velocity = m_kart->getKartProperties()->getRescueHeight() / m_timer; m_xyz = m_kart->getXYZ(); From 393aa86c31a75904d105fce40d1d1c03fca22025 Mon Sep 17 00:00:00 2001 From: nixt Date: Tue, 29 Jul 2014 01:53:21 +0530 Subject: [PATCH 081/350] Fix explosion animation --- src/karts/explosion_animation.cpp | 10 +++++----- src/karts/explosion_animation.hpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/karts/explosion_animation.cpp b/src/karts/explosion_animation.cpp index 53cb0e198..2843a294d 100644 --- a/src/karts/explosion_animation.cpp +++ b/src/karts/explosion_animation.cpp @@ -76,7 +76,7 @@ ExplosionAnimation::ExplosionAnimation(AbstractKart *kart, : AbstractKartAnimation(kart, "ExplosionAnimation") { m_xyz = m_kart->getXYZ(); - m_orig_y = m_xyz.getY(); + m_orig_xyz = m_xyz; m_kart->playCustomSFX(SFXManager::CUSTOM_EXPLODE); m_timer = m_kart->getKartProperties()->getExplosionTime(); @@ -144,13 +144,13 @@ ExplosionAnimation::~ExplosionAnimation() void ExplosionAnimation::update(float dt) { m_velocity -= dt*World::getWorld()->getTrack()->getGravity(); - - m_xyz.setY(m_xyz.getY() + dt*m_velocity); + Vec3 normal = -1.0f*m_kart->getBody()->getGravity().normalized(); + m_xyz = Vec3(m_xyz + dt*m_velocity*normal); // Make sure the kart does not end up under the track - if(m_xyz.getY() Date: Wed, 30 Jul 2014 23:55:37 +0530 Subject: [PATCH 082/350] Avoid an assertion failure when re-aligning items, the angle is zero. --- src/items/item.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/items/item.cpp b/src/items/item.cpp index 7d42c99df..4d5e058be 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -39,10 +39,10 @@ Item::Item(ItemType type, const Vec3& xyz, const Vec3& normal, m_distance_2 = 1.2f; initItem(type, xyz); - // Sets heading to 0, and sets pitch and roll depending on the normal. */ - m_original_hpr.setHPR(btQuaternion(-normal.cross(Vec3(0, 1, 0)), - normal.angle(Vec3(0, 1, 0))) - ); + //* Rotate item depending on the normal */ + if (normal.angle(Vec3(0, 1, 0))!= 0) + m_original_hpr.setHPR(btQuaternion(-normal.cross(Vec3(0, 1, 0)), + normal.angle(Vec3(0, 1, 0)))); m_original_mesh = mesh; m_original_lowmesh = lowres_mesh; m_listener = NULL; From c05840f9abd8c194a4f1eb443306b20858ad23ff Mon Sep 17 00:00:00 2001 From: nixt Date: Wed, 30 Jul 2014 23:56:57 +0530 Subject: [PATCH 083/350] Ground closeness is now checked using dist. between hitpoint and kart's xyz. Also disable rescue when debug flying. --- src/karts/kart.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 98add2321..6604db111 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -959,7 +959,7 @@ bool Kart::isOnGround() const */ bool Kart::isNearGround() const { - if(m_terrain_info->getHoT()==Track::NOHIT) + if((m_terrain_info->getHitPoint() - getXYZ()).length() ==Track::NOHIT) return false; else return ((getXYZ().getY() - m_terrain_info->getHoT()) @@ -1179,7 +1179,8 @@ void Kart::update(float dt) (!m_terrain_info->getMaterial() || !m_terrain_info->getMaterial()->hasGravity()) && !getKartAnimation() && fabs(roll)>60*DEGREE_TO_RAD && - fabs(getSpeed())<3.0f ) + fabs(getSpeed())<3.0f && + !m_flying) { new RescueAnimation(this, /*is_auto_rescue*/true); } From 21328d0e05309fa8e0ae115307899957ee9dc527 Mon Sep 17 00:00:00 2001 From: nixt Date: Thu, 31 Jul 2014 01:07:37 +0530 Subject: [PATCH 084/350] Fix a steering bug where the kart will try to steer back to the current quads center. --- src/karts/controller/skidding_ai.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index d904f2687..038e883f0 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -33,7 +33,7 @@ // Shows line from kart to its aim point # undef AI_DEBUG_KART_AIM #endif - +#define AI_DEBUG #include "karts/controller/skidding_ai.hpp" #ifdef AI_DEBUG @@ -380,7 +380,7 @@ void SkiddingAI::update(float dt) m_controls->m_fire = true; } } - + /*And obviously general kart stuff*/ AIBaseController::update(dt); } // update @@ -2052,7 +2052,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) Vec3 forw(0, 0, 50); m_curve[CURVE_KART]->addPoint(m_kart->getTrans()(forw)+eps); #endif - *last_node = m_next_node_index[m_track_node]; + *last_node = m_track_node; float angle = QuadGraph::get()->getAngleToNext(m_track_node, m_successor_index[m_track_node]); int target_sector; @@ -2136,7 +2136,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) if ( distance + m_kart_width * 0.5f > QuadGraph::get()->getNode((*last_node+j)%QuadGraph::get()->getNumNodes() ).getPathWidth()*0.5f ) { - *aim_position = QuadGraph::get()->getUnrolledQuadOfNode(m_track_node,future_successor_idx,j) + *aim_position = QuadGraph::get()->getUnrolledQuadOfNode(m_track_node,future_successor_idx,j+1) .getCenter(); return; } From fbf002bfc6a363de541fa6ce2896eda4581b42c2 Mon Sep 17 00:00:00 2001 From: nixt Date: Thu, 31 Jul 2014 18:15:37 +0530 Subject: [PATCH 085/350] Disable debug messages --- src/modes/linear_world.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index 119a739b6..ae06e383c 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -655,9 +655,9 @@ btTransform LinearWorld::getRescueTransform(unsigned int index) const btQuaternion r = q1*q2; btVector3 axis = r.getAxis(); float angle = r.getAngle(); - Log::verbose("rescue", "%d angle %f axis %f %f %f pos %f %f %f", - index, angle, axis.getX(),axis.getY(),axis.getZ(), - xyz.getX(),xyz.getY(),xyz.getZ()); + //Log::debug("rescue", "%d angle %f axis %f %f %f pos %f %f %f", + // index, angle, axis.getX(),axis.getY(),axis.getZ(), + // xyz.getX(),xyz.getY(),xyz.getZ()); #endif return pos; } // getRescueTransform From f40fb50386a87535c8d42309fdc5545d79d25da4 Mon Sep 17 00:00:00 2001 From: nixt Date: Fri, 1 Aug 2014 04:01:28 +0530 Subject: [PATCH 086/350] Fix "Wrong Direction" message --- src/modes/linear_world.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index ae06e383c..e861d2061 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -866,8 +866,10 @@ void LinearWorld::checkForWrongDirection(unsigned int i) return; // check if the player is going in the wrong direction - float angle_diff = kart->getHeading() - - m_track->getAngle(sector); + GraphNode& node = QuadGraph::get()->getNode(sector); + Vec3 center_line = node.getUpperCenter() - node.getLowerCenter(); + float angle_diff = kart->getVelocity().angle(center_line); + if(angle_diff > M_PI) angle_diff -= 2*M_PI; else if (angle_diff < -M_PI) angle_diff += 2*M_PI; // Display a warning message if the kart is going back way (unless From 8431fcfef20a634bd40f1beb25c4e84f6800f538 Mon Sep 17 00:00:00 2001 From: nixt Date: Sat, 2 Aug 2014 02:35:14 +0530 Subject: [PATCH 087/350] Prevent crash in modes where there is no QuadGraph --- src/karts/kart.cpp | 50 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 6604db111..ffff9a268 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1169,20 +1169,31 @@ void Kart::update(float dt) // But only do this if auto-rescue is enabled (i.e. it will be disabled in // battle mode), and the material the kart is driving on does not have // gravity (which can - - unsigned int sector = ((LinearWorld*)World::getWorld())->getTrackSector(getWorldKartId()).getCurrentGraphNode(); - const Vec3 quadNormal = QuadGraph::get()->getQuadOfNode(sector).getNormal(); - btQuaternion q = getTrans().getRotation(); - float roll = quadNormal.angle((Vec3(0, 1, 0).rotate(q.getAxis(), q.getAngle()))); - - if(World::getWorld()->getTrack()->isAutoRescueEnabled() && - (!m_terrain_info->getMaterial() || - !m_terrain_info->getMaterial()->hasGravity()) && - !getKartAnimation() && fabs(roll)>60*DEGREE_TO_RAD && - fabs(getSpeed())<3.0f && - !m_flying) + // For now we onyl do this if no battle mode. Later on we might merge the battle AI + // and have a unified interface and remove this if clause + unsigned int sector; + Vec3 quadNormal; + if (!QuadGraph::get()) { - new RescueAnimation(this, /*is_auto_rescue*/true); + sector = 0; + quadNormal = Vec3(0, 1, 0); + } + else + { + sector = ((LinearWorld*)World::getWorld())->getTrackSector(getWorldKartId()).getCurrentGraphNode(); + quadNormal = QuadGraph::get()->getQuadOfNode(sector).getNormal(); + btQuaternion q = getTrans().getRotation(); + float roll = quadNormal.angle((Vec3(0, 1, 0).rotate(q.getAxis(), q.getAngle()))); + + if (World::getWorld()->getTrack()->isAutoRescueEnabled() && + (!m_terrain_info->getMaterial() || + !m_terrain_info->getMaterial()->hasGravity()) && + !getKartAnimation() && fabs(roll) > 60 * DEGREE_TO_RAD && + fabs(getSpeed()) < 3.0f && + !m_flying) + { + new RescueAnimation(this, /*is_auto_rescue*/true); + } } // Add a certain epsilon (0.3) to the height of the kart. This avoids @@ -1209,6 +1220,7 @@ void Kart::update(float dt) m_terrain_info->update(getXYZ() + epsilon*(quadNormal), -quadNormal); + if(m_body->getBroadphaseHandle()) @@ -1229,7 +1241,15 @@ void Kart::update(float dt) // let kart fall a bit before rescuing const Vec3 *min, *max; World::getWorld()->getTrack()->getAABB(&min, &max); - if(min->getY() - getXYZ().getY() > 17 && !m_flying && + + // We do this for now because dist_to_sector is not defined + float dist_to_sector; + if (QuadGraph::get()) + dist_to_sector = getXYZ().distance(QuadGraph::get()->getQuadOfNode(sector).getCenter()); + else + dist_to_sector = 0; + + if((min->getY() - getXYZ().getY() > 17 || dist_to_sector > 25) && !m_flying && !getKartAnimation()) new RescueAnimation(this); } @@ -2044,7 +2064,7 @@ void Kart::updatePhysics(float dt) { Vec3 diff = getXYZ() - getTerrainInfo()->getHitPoint(); float height = diff.dot(getNormal()); - if(height>0.5f) + if(height>0.5f && !m_flying) { btVector3 kart_up = getTrans().getBasis().getColumn(1); // up vector btVector3 new_up = 0.9f * kart_up + 0.1f * getNormal(); From 058cec9f651186abb16d718ba3aa049572b7dcf8 Mon Sep 17 00:00:00 2001 From: nixt Date: Wed, 6 Aug 2014 00:19:53 +0530 Subject: [PATCH 088/350] Make items align with the track triangle instead of the driveline quad. --- src/items/flyable.cpp | 2 +- src/tracks/track.cpp | 33 +++++++++++++++++++++++---------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/items/flyable.cpp b/src/items/flyable.cpp index 5651180be..f4a805000 100644 --- a/src/items/flyable.cpp +++ b/src/items/flyable.cpp @@ -388,7 +388,7 @@ bool Flyable::updateAndDelete(float dt) // Add the position offset so that the flyable can adjust its position // (usually to do the raycast from a slightly higher position to avoid // problems finding the terrain in steep uphill sections). - // towards is a unit vector. so we can multiply -towards to offset the position + // Towards is a unit vector. so we can multiply -towards to offset the position // by one unit. TerrainInfo::update(xyz + m_position_offset*(-towards), towards); if (race_manager->getMinorMode() != RaceManager::MINOR_MODE_3_STRIKES && diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index 511d0e068..9e897740e 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -2315,12 +2315,20 @@ void Track::itemCommand(const XMLNode *node) } // Tilt the items according to the track - int road_sector = QuadGraph::UNKNOWN_SECTOR; - QuadGraph::get()->findRoadSector(xyz, &road_sector); - // If a valid road_sector is not found - if (road_sector == QuadGraph::UNKNOWN_SECTOR) - road_sector=QuadGraph::get()->findOutOfRoadSector(xyz, road_sector); - Vec3 normal = QuadGraph::get()->getQuadOfNode(road_sector).getNormal(); + Vec3 normal(0,1,0); + if (QuadGraph::get()) + { + int road_sector = QuadGraph::UNKNOWN_SECTOR; + QuadGraph::get()->findRoadSector(xyz, &road_sector); + // If a valid road_sector is not found + if (road_sector == QuadGraph::UNKNOWN_SECTOR) + road_sector = QuadGraph::get()->findOutOfRoadSector(xyz, road_sector); + Vec3 quadnormal = QuadGraph::get()->getQuadOfNode(road_sector).getNormal(); + + const Material *m; + Vec3 hit_point; + m_track_mesh->castRay(loc, loc + -1.0f*quadnormal, &hit_point, &m, &normal); + } ItemManager::get()->newItem(type, loc, normal); } // itemCommand @@ -2403,11 +2411,16 @@ const core::vector3df& Track::getSunRotation() bool Track::findGround(AbstractKart *kart) { btVector3 to(kart->getXYZ()); - unsigned int sector = ((LinearWorld*)World::getWorld())->getTrackSector(kart->getWorldKartId()).getCurrentGraphNode(); Vec3 quadNormal; - if (sector != QuadGraph::UNKNOWN_SECTOR) - quadNormal = QuadGraph::get()->getQuadOfNode(sector).getNormal(); - else quadNormal = Vec3(0, 1, 0); + if (QuadGraph::get()) + { + unsigned int sector = ((LinearWorld*)World::getWorld())->getTrackSector(kart->getWorldKartId()).getCurrentGraphNode(); + if (sector != QuadGraph::UNKNOWN_SECTOR) + quadNormal = QuadGraph::get()->getQuadOfNode(sector).getNormal(); + } + else + quadNormal = Vec3(0, 1, 0); + to = to + -1000.0f*quadNormal; // Material and hit point are not needed; From 1f4ecc2464906efd8ee8fd6279f86f07c789c47d Mon Sep 17 00:00:00 2001 From: nixt Date: Thu, 7 Aug 2014 01:38:01 +0530 Subject: [PATCH 089/350] Cleanup irr_driver.cpp and remove ai_debug from skidding_ai --- src/graphics/irr_driver.cpp | 3 +-- src/karts/controller/skidding_ai.cpp | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 7b82dcb97..1d49571de 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -449,8 +449,7 @@ void IrrDriver::initDevice() Log::info("IrrDriver", "OpenGL version string: %s", glGetString(GL_VERSION)); m_glsl = (GLMajorVersion > 3 || (GLMajorVersion == 3 && GLMinorVersion >= 1)); } - m_glsl = false; - + // Parse extensions hasVSLayer = false; // Default false value for hasVSLayer if --no-graphics argument is used diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index 038e883f0..64096565e 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -33,7 +33,7 @@ // Shows line from kart to its aim point # undef AI_DEBUG_KART_AIM #endif -#define AI_DEBUG + #include "karts/controller/skidding_ai.hpp" #ifdef AI_DEBUG From b4b2f6988ba229359bf36b656ae06edf9f6d6c14 Mon Sep 17 00:00:00 2001 From: nixt Date: Thu, 7 Aug 2014 15:28:21 +0530 Subject: [PATCH 090/350] Add documentation and some cleanup. --- src/karts/controller/skidding_ai.cpp | 39 ++++++++++++++-------------- src/tracks/graph_node.cpp | 34 +++++++++++++++++------- src/tracks/quad.cpp | 27 ++++++------------- src/tracks/quad_graph.hpp | 12 ++++++--- 4 files changed, 59 insertions(+), 53 deletions(-) diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index 64096565e..64c28e901 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -2020,14 +2020,6 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) /** This is basically the original AI algorithm. It is clearly buggy: * 1. the test: * - * distance + m_kart_width * 0.5f - * > QuadGraph::get()->getNode(*last_node).getPathWidth() ) - * - * is incorrect, it should compare with getPathWith*0.5f (since distance - * is the distance from the center, i.e. it is half the path width if - * the point is at the edge). - * 2. the test: - * * QuadGraph::get()->spatialToTrack(&step_track_coord, step_coord, * *last_node ); * in the for loop tests always against distance from the same @@ -2040,6 +2032,13 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) * which takes some time - so it is actually mostly on track. * Since this algoritm (so far) ends up with by far the best AI behaviour, * it is for now the default). + * + * GSoC 2014: This algorithm has been updated so that the AI can drive on + * upside-down tracks. Because steering only works in 2D, you can steer left and + * right but not up and down, but the track might not be 2D. So for every quad, + * we have a set of k unrolled quads starting from the current quad. The AI uses + * these unrolled quads to search for a suitable aim point. + * * \param aim_position On exit contains the point the AI should aim at. * \param last_node On exit contais the graph node the AI is aiming at. */ @@ -2062,6 +2061,11 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) float angle1; + // We need to find out if there is an alternate path that the kart might take. + // This is done in advance so that the right set of unrolled quads are used + // and the AI finds aim_points on the alternate path. future_successor_idx + // is successor index of a future node. 0 is the default, but if there is an + // alternate path coming up, this will be set to 1. int future_successor_idx = 0; int current = m_track_node; for (unsigned int j = 0; j < QuadGraph::get()->getNumberOfUnrolledQuads(); j++) @@ -2072,9 +2076,8 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) current = m_next_node_index[current]; } - // The original while(1) loop is replaced with a for loop to avoid - // infinite loops (which we had once or twice). Usually the number - // of iterations in the while loop is less than 7. + // This for loop runs till it runs out of unrolled quads or breaks when the + // aim point is outside one of the unrolled quads. for(unsigned int j=0; jgetNumberOfUnrolledQuads(); j++) { // target_sector is the sector at the longest distance that we can @@ -2118,18 +2121,14 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) } Vec3 step_coord; - //Test if we crash if we drive towards the target sector + //Test if we crash if we drive towards the target sector (unrolled set) for(unsigned int i = 2; i < steps; ++i ) { step_coord = m_kart->getXYZ()+direction*m_kart_length * float(i); - //QuadGraph::get()->spatialToTrack(&step_track_coord, step_coord, - // *last_node ); QuadGraph::get()->spatialToTrackUnrolled(&step_track_coord, step_coord, m_track_node, j, future_successor_idx); - - //float distance = fabsf(step_track_coord[0]); float distance = step_track_coord[0]; //If we are outside, the previous node is what we are looking for @@ -2140,15 +2139,15 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) .getCenter(); return; } - - + } angle = angle1; *last_node = target_sector; - } // for i<100 + } + // If we are inside entire unrolled set then set the aim_point to be center of the last unrolled quad *aim_position = QuadGraph::get()->getNode(m_track_node). getUnrolledQuad(future_successor_idx,QuadGraph::get()->getNumberOfUnrolledQuads()).getCenter(); - //*aim_position = QuadGraph::get()->getQuadOfNode(*last_node).getCenter(); + } // findNonCrashingPoint //----------------------------------------------------------------------------- diff --git a/src/tracks/graph_node.cpp b/src/tracks/graph_node.cpp index f8c8ede3c..c3fc19a6d 100644 --- a/src/tracks/graph_node.cpp +++ b/src/tracks/graph_node.cpp @@ -186,7 +186,7 @@ void GraphNode::setDirectionData(unsigned int successor, DirectionType dir, /** Returns the distance a point has from this quad in forward and sidewards * direction, i.e. how far forwards the point is from the beginning of the * quad, and how far to the side from the line connecting the center points - * is it. All these computations are done in 2D only. + * is it. * \param xyz The coordinates of the point. * \param result The X coordinate contains the sidewards distance, the * Z coordinate the forward distance. @@ -205,6 +205,17 @@ void GraphNode::getDistances(const Vec3 &xyz, Vec3 *result) (closest-m_lower_center.toIrrVector()).getLength()); } // getDistances +// ---------------------------------------------------------------------------- +/** Returns the distance a point has from an unrolled quad in forward and sidewards + * direction, i.e. how far forwards the point is from the beginning of the + * quad, and how far to the side from the line connecting the center points + * is it. + * \param xyz The coordinates of the point. + * \param fork_number The fork on which the unrolled quad lies. + * \param quad_idx The index of the unrolled quad to find distances from. + * \param result The X coordinate contains the sidewards distance, the + * Z coordinate the forward distance. + */ void GraphNode::getDistancesUnrolled(const Vec3 &xyz, const int fork_number, unsigned int quad_idx, Vec3 *result) { @@ -262,7 +273,12 @@ const Vec3 GraphNode::getPointTransformedToFlatQuad(Vec3 xyz) return (Vec3)result; } - +// ---------------------------------------------------------------------------- +/** This functions builds unrolled quads for this node. This takes into account + * if there is a fork in coming up. In this case there will be two sets of + * unrolled quads for this node. One for each fork. + * \param unroll_quad_count The length of the unrolled set of quads per node. + */ void GraphNode::buildUnrolledQuads(unsigned int unroll_quad_count) { m_unrolled_quads.clear(); @@ -296,7 +312,11 @@ void GraphNode::buildUnrolledQuads(unsigned int unroll_quad_count) } } - +// ---------------------------------------------------------------------------- +/** This function is called recursively to build one set of unrolled quads. Each + * call adds one unrolled quad to the existing set. + * \param unroll_quad_count The length of the unrolled set of quads per node. + */ void GraphNode::addUnrolledQuad(const GraphNode& next_node, int fork_number, int k) { if (k == 0) return; @@ -313,11 +333,7 @@ void GraphNode::addUnrolledQuad(const GraphNode& next_node, int fork_number, int // in the vector of unrolled quads m.buildRotateFromTo(next_quad_to_push.getNormal().toIrrVector(), last_pushed_quad.getNormal().toIrrVector()); - - - //m.setRotationCenter(next_quad_to_push.getCenter().toIrrVector(), - // ((next_quad_to_push.getCenter() - 0.5f*(next_quad_to_push[0] + next_quad_to_push[1])) + 0.5f*(last_pushed_quad[2] + last_pushed_quad[3))); - + for (unsigned int i = 0; i < 4; i++) m.rotateVect(new_points[i], next_quad_to_push[i].toIrrVector()); @@ -337,8 +353,6 @@ void GraphNode::addUnrolledQuad(const GraphNode& next_node, int fork_number, int for (unsigned int i = 0; i < 4; i++) m.rotateVect(new_points[i]); - - // Next translate the new quad to be pushed to the correct position infront // of the last quad in the vector of unrolled quads Vec3 lower_center, upper_center; diff --git a/src/tracks/quad.cpp b/src/tracks/quad.cpp index 6f74a0662..bcc712c35 100644 --- a/src/tracks/quad.cpp +++ b/src/tracks/quad.cpp @@ -30,22 +30,9 @@ Quad::Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, bool invisible, bool ai_ignore) { + + m_p[0]=p0; m_p[1]=p1; m_p[2]=p2; m_p[3]=p3; - /*This check must be skipped because inverted quads may appear to have - incorrect oritentation. - if(p1.sideOfLine2D(p0, p2)>0 || - p3.sideOfLine2D(p0, p2)<0) - { - Log::warn("Quad", "Quad has wrong orientation: p0=%f %f %f p1=%f %f %f", - p0.getX(), p0.getY(), p0.getZ(),p1.getX(), p1.getY(), p1.getZ()); - Log::warn("Quad", "The quad will be swapped, nevertheless test for correctness -"); - Log::warn("Quad", "quads must be counter-clockwise oriented."); - m_p[0]=p1; m_p[1]=p0; m_p[2]=p3; m_p[3]=p2; - } - else - {*/ - m_p[0]=p0; m_p[1]=p1; m_p[2]=p2; m_p[3]=p3; - //} m_center = 0.25f*(p0+p1+p2+p3); m_min_height = std::min ( std::min(p0.getY(), p1.getY()), std::min(p2.getY(), p3.getY()) ); @@ -179,6 +166,10 @@ void Quad::transform(const btTransform &t, Quad *result) const result->m_p[3].getY()) ); } // transform +// ---------------------------------------------------------------------------- +/** Find the normal of this quad by computing the normal of two triangles and taking + * their average. + */ void Quad::findNormal() { core::triangle3df tri1(m_p[0].toIrrVector(), m_p[1].toIrrVector(), m_p[2].toIrrVector()); @@ -189,6 +180,8 @@ void Quad::findNormal() m_normal.normalize(); } +// ---------------------------------------------------------------------------- +/** Return a flattened version of this quad. */ Quad Quad::getFlattenedQuad() { core::CMatrix4 m; @@ -196,10 +189,6 @@ Quad Quad::getFlattenedQuad() Vec3 m_p_flat[4]; for (unsigned int i = 0; i < 4; i++) { - // Translate to origin - //m_p_flat[i] = m_p[i] - m_center; - - // rotateVect(out,in) m.rotateVect(m_p_flat[i], (m_p[i] - m_center).toIrrVector()); m_p_flat[i].setY(0); diff --git a/src/tracks/quad_graph.hpp b/src/tracks/quad_graph.hpp index 230b68cbc..87ae0e484 100644 --- a/src/tracks/quad_graph.hpp +++ b/src/tracks/quad_graph.hpp @@ -78,7 +78,8 @@ private: /** Wether the graph should be reverted or not */ bool m_reverse; - + + /** Number of unrolled quads to compute per quad */ unsigned int m_unroll_quad_count; void setDefaultSuccessors(); @@ -202,10 +203,13 @@ public: // ---------------------------------------------------------------------- /** Returns true if the graph is to be reversed. */ bool isReverse() const {return m_reverse; } - - const Quad& getUnrolledQuadOfNode(unsigned int node, unsigned int fork_number, unsigned int quad_number) + // ---------------------------------------------------------------------- + /** Returns a unrolled quad of a node. */ + const Quad& getUnrolledQuadOfNode(unsigned int node, + unsigned int fork_number, + unsigned int quad_number) { return getNode(node).getUnrolledQuad(fork_number,quad_number); } - + // ---------------------------------------------------------------------- /** Returns the number of forward quads that are unrolled for each quad **/ int getNumberOfUnrolledQuads() const { return m_unroll_quad_count; } From e3dea1da0203e1ddcd03993c7267d6f359c703ad Mon Sep 17 00:00:00 2001 From: nixt Date: Thu, 7 Aug 2014 15:34:20 +0530 Subject: [PATCH 091/350] Forgot to add // functionName --- src/items/rubber_ball.cpp | 15 ++++++--------- src/tracks/graph_node.cpp | 8 ++++---- src/tracks/quad.cpp | 10 +++++++--- src/tracks/quad.hpp | 8 ++++++-- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/items/rubber_ball.cpp b/src/items/rubber_ball.cpp index 42a1ef467..21a83f0bd 100644 --- a/src/items/rubber_ball.cpp +++ b/src/items/rubber_ball.cpp @@ -582,15 +582,12 @@ float RubberBall::updateHeight() } // updateHeight // ---------------------------------------------------------------------------- -/** Returns the maximum height of the terrain at the current point. While - * generall the height is arbitrary (a skybox is not part of the physics and - * will therefore not be detected), it is important that a rubber ball does - * not end up on top of a tunnel. +/** When the ball is in a tunnel, this will return the tunnel height. + * NOTE: When this function is called next_xyz is usually the interpolated point + * on the track and not the ball's current location. Look at updateAndDelete(). + * * \param vertical_offset A vertical offset which is added to the current - * position of the kart in order to avoid tunneling effects (it could - * happen that the raycast down find the track since it uses the - * vertical offset, while the raycast up would hit under the track - * if the vertical offset is not used). + * position in order to avoid hitting the track when doing a raycast up. * \returns The distance to the terrain element found by raycast in the up direction. If no terrain found, it returns 99990 */ @@ -604,7 +601,7 @@ float RubberBall::getTunnelHeight(const Vec3 &next_xyz, const float vertical_off tm.castRay(from, to, &hit_point, &material); return (material) ? (hit_point - next_xyz).length() : 99999.f; -} // getMaxTerrainHeight +} // getTunnelHeight // ---------------------------------------------------------------------------- /** Determines the distance to the target kart. If the target is close, the diff --git a/src/tracks/graph_node.cpp b/src/tracks/graph_node.cpp index c3fc19a6d..a1bf3810f 100644 --- a/src/tracks/graph_node.cpp +++ b/src/tracks/graph_node.cpp @@ -237,7 +237,7 @@ void GraphNode::getDistancesUnrolled(const Vec3 &xyz, const int fork_number, uns result->setZ(QuadGraph::get()->getNode((m_node_index + quad_idx)%QuadGraph::get()->getNumNodes()).getDistanceFromStart() + (xyz - lower_center).length()); -} +} // getDistancesUnrolled // ---------------------------------------------------------------------------- /** Returns the square of the distance between the given point and any point @@ -271,7 +271,7 @@ const Vec3 GraphNode::getPointTransformedToFlatQuad(Vec3 xyz) m.rotateVect(result, (xyz - thisQuad.getCenter()).toIrrVector()); return (Vec3)result; -} +} // ---------------------------------------------------------------------------- /** This functions builds unrolled quads for this node. This takes into account @@ -310,7 +310,7 @@ void GraphNode::buildUnrolledQuads(unsigned int unroll_quad_count) GraphNode& next = QuadGraph::get()->getNode(getSuccessor(i%getNumberOfSuccessors())); addUnrolledQuad(next , i , unroll_quad_count); } -} +} // buildUnrolledQuads // ---------------------------------------------------------------------------- /** This function is called recursively to build one set of unrolled quads. Each @@ -377,4 +377,4 @@ void GraphNode::addUnrolledQuad(const GraphNode& next_node, int fork_number, int // Recurisvely build the vector of unrolled quads till k reduces to 0 GraphNode& next = QuadGraph::get()->getNode(next_node.getSuccessor(fork_number%next_node.getNumberOfSuccessors())); addUnrolledQuad(next, fork_number, k); -} +} // addUnrolledQuad diff --git a/src/tracks/quad.cpp b/src/tracks/quad.cpp index bcc712c35..9b277f9fd 100644 --- a/src/tracks/quad.cpp +++ b/src/tracks/quad.cpp @@ -134,6 +134,8 @@ bool Quad::pointInQuad(const Vec3& p) const } } // pointInQuad +// ---------------------------------------------------------------------------- +/** Checks if a given point p lies in the bounding box of this quad */ bool Quad::pointInQuad3D(const Vec3& p) const { float side = p.sideofPlane(m_box_faces[0][0], m_box_faces[0][1], m_box_faces[0][2]); @@ -142,7 +144,7 @@ bool Quad::pointInQuad3D(const Vec3& p) const if (side*p.sideofPlane(m_box_faces[i][0], m_box_faces[i][1], m_box_faces[i][2]) < 0) return false; } return true; -} +} // pointInQuad3D // ---------------------------------------------------------------------------- @@ -178,7 +180,8 @@ void Quad::findNormal() Vec3 normal2 = tri2.getNormal(); m_normal = -0.5f*(normal1 + normal2); m_normal.normalize(); -} + +} // findNormal // ---------------------------------------------------------------------------- /** Return a flattened version of this quad. */ @@ -195,4 +198,5 @@ Quad Quad::getFlattenedQuad() } return Quad(m_p_flat[0], m_p_flat[1], m_p_flat[2], m_p_flat[3]); -} + +} // getFlattenedQuad diff --git a/src/tracks/quad.hpp b/src/tracks/quad.hpp index 951399697..ad98dae31 100644 --- a/src/tracks/quad.hpp +++ b/src/tracks/quad.hpp @@ -61,8 +61,10 @@ private: /** Set if this quad should not be used by the AI. */ bool m_ai_ignore; + /** For each quad, construct a 3D box to check if a point lies inside it. */ Vec3 m_box_faces[6][4]; + /** Find the normal of this quad */ void findNormal(); public: @@ -88,9 +90,11 @@ public: // ------------------------------------------------------------------------ /** True if this quad should be ignored by the AI. */ bool letAIIgnore() const { return m_ai_ignore; } - + // ------------------------------------------------------------------------ + /** Returns the normal of this quad */ const Vec3& getNormal() const { return m_normal; } - + // ------------------------------------------------------------------------ + /** Return a flattened version of this quad */ Quad getFlattenedQuad(); }; // class Quad From c4cfb831503f184e0f1b2cd75ff846a01485b644 Mon Sep 17 00:00:00 2001 From: nixt Date: Sat, 9 Aug 2014 13:06:42 +0530 Subject: [PATCH 092/350] enable sliding for upside down tracks. --- src/karts/kart.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 26da7225c..a212aa07b 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -2281,10 +2281,21 @@ void Kart::updateSliding() (!getMaterial() || !getMaterial()->highTireAdhesion())) { const btMatrix3x3 &m = m_vehicle->getChassisWorldTransform().getBasis(); - // To get the angle between up=(0,1,0), we have to do: - // m*(0,1,0) to get the up vector of the kart, then the - // scalar product between this and (0,1,0) - which is m[1][1]: - float distanceFromUp = m[1][1]; + float distanceFromUp; + if (QuadGraph::get()) + { + int sector = ((LinearWorld*)World::getWorld())->getTrackSector(getWorldKartId()).getCurrentGraphNode(); + Vec3 quadNormal = QuadGraph::get()->getQuadOfNode(sector).getNormal(); + Vec3 kart_up = m.getColumn(1); + distanceFromUp = kart_up.dot(quadNormal); + } + else + { + // To get the angle between up=(0,1,0), we have to do: + // m*(0,1,0) to get the up vector of the kart, then the + // scalar product between this and (0,1,0) - which is m[1][1]: + distanceFromUp = m[1][1]; + } if (distanceFromUp < 0.85f) { From cfa8e29523367ee541f9db87db0e89b570a2a416 Mon Sep 17 00:00:00 2001 From: nixt Date: Sat, 9 Aug 2014 13:07:07 +0530 Subject: [PATCH 093/350] Add comments. --- src/tracks/quad_graph.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/tracks/quad_graph.cpp b/src/tracks/quad_graph.cpp index 04ca713d4..cc00ac1cb 100644 --- a/src/tracks/quad_graph.cpp +++ b/src/tracks/quad_graph.cpp @@ -534,7 +534,9 @@ void QuadGraph::createMesh(bool show_invisible, } // createMesh // ----------------------------------------------------------------------------- -/** Creates the actual mesh that is used by createDebugMesh() */ +/** This is used to draw one set of unrolled quads. This function is mostly a + a copy of createMesh used to draw the entire driveline. + FIXME: Either remove this function or name it appropirately*/ void QuadGraph::createMesh2() { // The debug track will not be lighted or culled. @@ -610,7 +612,7 @@ void QuadGraph::createMesh2() m_mesh_buffer->recalculateBoundingBox(); m_mesh->setBoundingBox(m_mesh_buffer->getBoundingBox()); -} // createMesh +} // createMesh2 From 887a86d3a6c4fb711ec354f2fa6798191e2ae197 Mon Sep 17 00:00:00 2001 From: nixt Date: Sat, 9 Aug 2014 13:09:47 +0530 Subject: [PATCH 094/350] Only set kart gravity when the material sets gravity. --- src/karts/kart.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index a212aa07b..167ca5764 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1262,7 +1262,7 @@ void Kart::update(float dt) Vec3 gravity(0.0f, -g, 0.0f); btRigidBody *body = getVehicle()->getRigidBody(); // If the material should overwrite the gravity, - if (material->hasGravity() || 1) + if (material->hasGravity()) { Vec3 normal = m_terrain_info->getNormal(); gravity = normal * -g; From d914e1eea3bc4642fd72fb4cfe97a8a4af87fca8 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 8 Apr 2016 15:44:50 +0800 Subject: [PATCH 095/350] Make game playable when start racing --- src/modes/linear_world.cpp | 4 ++-- src/tracks/track.cpp | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index 2bd68a78d..0d880c597 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -880,9 +880,9 @@ void LinearWorld::checkForWrongDirection(unsigned int i, float dt) Vec3 center_line = node.getUpperCenter() - node.getLowerCenter(); float angle_diff = kart->getVelocity().angle(center_line); - if (angle_diff > M_PI) + if (angle_diff > M_PI) angle_diff -= 2*M_PI; - else if (angle_diff < -M_PI) + else if (angle_diff < -M_PI) angle_diff += 2*M_PI; // Display a warning message if the kart is going back way (unless diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index 2fa18903c..7acdf202b 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -2388,15 +2388,13 @@ const core::vector3df& Track::getSunRotation() bool Track::findGround(AbstractKart *kart) { btVector3 to(kart->getXYZ()); - Vec3 quadNormal; + Vec3 quadNormal(0, 1, 0);; if (QuadGraph::get()) { - unsigned int sector = ((LinearWorld*)World::getWorld())->getTrackSector(kart->getWorldKartId()).getCurrentGraphNode(); + int sector = ((LinearWorld*)World::getWorld())->getTrackSector(kart->getWorldKartId()).getCurrentGraphNode(); if (sector != QuadGraph::UNKNOWN_SECTOR) quadNormal = QuadGraph::get()->getQuadOfNode(sector).getNormal(); } - else - quadNormal = Vec3(0, 1, 0); to = to + -1000.0f*quadNormal; @@ -2434,9 +2432,7 @@ bool Track::findGround(AbstractKart *kart) return false; } - btTransform t = kart->getBody()->getCenterOfMassTransform(); - btQuaternion q = t.getRotation(); // The computer offset is slightly too large, it should take // the default suspension rest instead of suspension rest (i.e. the // length of the suspension with the weight of the kart resting on From 8763c3aca7dda4c49f05a8d2d1a5c7a7a0abc5d6 Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 12 Apr 2016 17:37:14 +1000 Subject: [PATCH 096/350] Changed rewind key from F1 to F11 (F1 always opens the in-game menu). --- src/input/input_manager.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/input/input_manager.cpp b/src/input/input_manager.cpp index 28dc47dce..faa7290d6 100644 --- a/src/input/input_manager.cpp +++ b/src/input/input_manager.cpp @@ -274,8 +274,7 @@ void InputManager::handleStaticAction(int key, int value) if (value ==0 ) irr_driver->requestScreenshot(); break; - /* - case KEY_F1: + case KEY_F11: if(value && shift_is_pressed && world && RewindManager::isEnabled()) { printf("Enter rewind time:"); @@ -287,6 +286,9 @@ void InputManager::handleStaticAction(int key, int value) Log::info("Rewind", "Rewinding from %f to %f", world->getTime(), world->getTime()-t); } + break; + + /* else if (UserConfigParams::m_artist_debug_mode && world) { AbstractKart* kart = world->getLocalPlayerKart(0); From c54caf3e03c14988b2fa22054e5f3d0044eff019 Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 12 Apr 2016 17:38:15 +1000 Subject: [PATCH 097/350] Fixed missing #include file, some code cleanup. --- src/network/rewind_manager.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index 356e4f288..5eb6e9460 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -18,6 +18,7 @@ #include "network/rewind_manager.hpp" +#include "graphics/irr_driver.hpp" #include "modes/world.hpp" #include "network/rewinder.hpp" #include "physics/physics.hpp" @@ -319,21 +320,23 @@ void RewindManager::rewindTo(float rewind_time) // Rewind to the required state // ---------------------------- float exact_rewind_time = m_rewind_info[state]->getTime(); - World::getWorld()->getPhysics()->getPhysicsWorld()->setLocalTime(m_rewind_info[state]->getLocalPhysicsTime()); + float local_physics_time = m_rewind_info[state]->getLocalPhysicsTime(); + World *world = World::getWorld(); + world->getPhysics()->getPhysicsWorld()->setLocalTime(local_physics_time); // Rewind all objects (and also all state) that happen at the // current time. - // TODO: this assumes atm that all rewinder have a initial state + // TODO: this assumes atm that all rewinder have an initial state // for 'current_time'!!! // Store the time to which we have to replay to - float current_time = World::getWorld()->getTime(); + float current_time = world->getTime(); - World::getWorld()->setTime(exact_rewind_time); + world->setTime(exact_rewind_time); // Now go forward through the saved states, and // replay taking the events into account. - while( World::getWorld()->getTime() < current_time && + while( world->getTime() < current_time && state < (int)m_rewind_info.size() ) { @@ -341,7 +344,7 @@ void RewindManager::rewindTo(float rewind_time) // Now set all events and confirmed states while(state < (int)m_rewind_info.size() && - m_rewind_info[state]->getTime()<=World::getWorld()->getTime()+0.001f) + m_rewind_info[state]->getTime()<=world->getTime()+0.001f) { if(m_rewind_info[state]->isEvent() || m_rewind_info[state]->isConfirmed() ) @@ -350,7 +353,7 @@ void RewindManager::rewindTo(float rewind_time) } state++; } - World::getWorld()->updateWorld(dt); + world->updateWorld(dt); #define SHOW_ROLLBACK #ifdef SHOW_ROLLBACK irr_driver->update(dt); From 7a9d283f4360b1ff0946c1c6546b66ad79bababa Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 12 Apr 2016 22:35:56 +1000 Subject: [PATCH 098/350] Fixed comments. --- src/karts/kart_rewinder.cpp | 2 +- src/network/rewind_manager.cpp | 4 ++-- src/network/rewind_manager.hpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/karts/kart_rewinder.cpp b/src/karts/kart_rewinder.cpp index c247676f9..48d78594d 100644 --- a/src/karts/kart_rewinder.cpp +++ b/src/karts/kart_rewinder.cpp @@ -62,7 +62,7 @@ int KartRewinder::getState(char **buffer) const } // getState // ---------------------------------------------------------------------------- -/** Actuall rewind to the specified state. */ +/** Actually rewind to the specified state. */ void KartRewinder::rewindToState(char *buffer) { btTransform t; diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index 5eb6e9460..6a1bc020c 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -54,7 +54,7 @@ RewindManager::RewindInfo::RewindInfo(Rewinder *rewinder, char *buffer, bool is_event, bool is_confirmed) { m_rewinder = rewinder; - m_time = RewindManager::get()->getCurrentTime();; + m_time = RewindManager::get()->getCurrentTime(); m_time_step = RewindManager::get()->getCurrentTimeStep(); m_local_physics_time = World::getWorld()->getPhysics()->getPhysicsWorld()->getLocalTime(); m_buffer = buffer; @@ -290,7 +290,7 @@ void RewindManager::rewindTo(float rewind_time) if(!m_rewind_info[state]->isState()) { - Log::error("RewindManager", "No state for rewind to %d, state %d.", + Log::error("RewindManager", "No state for rewind to %f, state %d.", rewind_time, state); return; } diff --git a/src/network/rewind_manager.hpp b/src/network/rewind_manager.hpp index 7847949e6..195ed0c84 100644 --- a/src/network/rewind_manager.hpp +++ b/src/network/rewind_manager.hpp @@ -34,9 +34,9 @@ * on this client, which can save time in rewinding later). * - events for each rewindable object (for example any change in the kart * controls, like steering, fire, ... are an event). While states can be - * discarded (especially unconfirmed ones), e.g. to save spave, events + * discarded (especially unconfirmed ones), e.g. to save space, events * will always be kept (in order to allow replaying). - * Each object that is to be rewinded an instance of Rewinder needs to be + * For each object that is to be rewinded an instance of Rewinder needs to be * declared (usually inside of the object it can rewind). This instance * is automatically registered with the RewindManager. * All states and events are stored in a RewindInfo object. All RewindInfo From a0623aee578fd6d72641c6091349066559e6d5b4 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 13 Apr 2016 07:40:47 +1000 Subject: [PATCH 099/350] Force fresh cmake configuration. --- sources.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources.cmake b/sources.cmake index ddc029d4f..f484b15d5 100644 --- a/sources.cmake +++ b/sources.cmake @@ -1,5 +1,5 @@ # Modify this file to change the last-modified date when you add/remove a file. -# This will then trigger a new cmake run automatically. +# This will then trigger a new cmake run automatically. file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp") file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp") file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*") From 2f0e60694ccb68b7b95f22a36592453e4bbfe9ed Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 13 Apr 2016 08:01:24 +1000 Subject: [PATCH 100/350] Removed duplicated function. --- src/karts/controller/kart_control.hpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/karts/controller/kart_control.hpp b/src/karts/controller/kart_control.hpp index af761f23e..d947b4ae8 100644 --- a/src/karts/controller/kart_control.hpp +++ b/src/karts/controller/kart_control.hpp @@ -102,14 +102,7 @@ public: } // setFromMemory // ------------------------------------------------------------------------ - void uncompress(char *c) - { - m_steer = ((float*)c)[0]; - m_accel = ((float*)c)[1]; - setButtonsCompressed(c[8]); - } // uncompress - // ------------------------------------------------------------------------ - /** Compresses all buttons into a single integer value. */ + /** Compresses all buttons into a single byte. */ char getButtonsCompressed() const { return (m_brake ? 1 : 0) @@ -120,7 +113,7 @@ public: + (m_skid<<5); // m_skid is in {0,1,2,3} } // getButtonsCompressed // ------------------------------------------------------------------------ - /** Sets the buttons from a compressed representation. + /** Sets the buttons from a compressed (1 byte) representation. * /param c Character containing the compressed representation. */ void setButtonsCompressed(char c) From f9b0b05d53c60b7345bfbcf923416a7a32a982bb Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 14 Apr 2016 10:40:15 +0800 Subject: [PATCH 101/350] Fix warning --- src/items/item.cpp | 8 ++++++++ src/karts/controller/skidding_ai.cpp | 20 ++++++++------------ src/tracks/graph_node.cpp | 5 ++--- src/tracks/quad_graph.cpp | 12 ++++++++---- src/tracks/quad_graph.hpp | 2 +- 5 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/items/item.cpp b/src/items/item.cpp index 5a00f3bca..8f5a0c07b 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -41,8 +41,16 @@ Item::Item(ItemType type, const Vec3& xyz, const Vec3& normal, initItem(type, xyz); //* Rotate item depending on the normal */ if (normal.angle(Vec3(0, 1, 0))!= 0) + { m_original_hpr.setHPR(btQuaternion(-normal.cross(Vec3(0, 1, 0)), normal.angle(Vec3(0, 1, 0)))); + } + else + { + // Sets heading to 0, and sets pitch and roll depending on the normal. */ + m_original_hpr = Vec3(0, normal); + } + m_original_mesh = mesh; m_original_lowmesh = lowres_mesh; m_listener = NULL; diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index 2ec3ce85e..67db4b036 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -2055,7 +2055,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) * \param aim_position On exit contains the point the AI should aim at. * \param last_node On exit contais the graph node the AI is aiming at. */ - void SkiddingAI::findNonCrashingPoint(Vec3 *aim_position, int *last_node) +void SkiddingAI::findNonCrashingPoint(Vec3 *aim_position, int *last_node) { #ifdef AI_DEBUG_KART_HEADING const Vec3 eps(0,0.5f,0); @@ -2068,7 +2068,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) float angle = QuadGraph::get()->getAngleToNext(m_track_node, m_successor_index[m_track_node]); int target_sector; - + Vec3 direction; Vec3 step_track_coord; @@ -2096,7 +2096,6 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) // target_sector is the sector at the longest distance that we can // drive to without crashing with the track. target_sector = m_next_node_index[*last_node]; - angle1 = QuadGraph::get()->getAngleToNext(target_sector, m_successor_index[target_sector]); @@ -2110,15 +2109,12 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) .getCenter(); return; } - - Quad target_quad_unrolled = QuadGraph::get()->getNode(m_track_node). getUnrolledQuad(future_successor_idx,j + 1); direction = target_quad_unrolled.getCenter() - m_kart->getXYZ(); - float len = direction.length(); unsigned int steps = (unsigned int)( len / m_kart_length ); if( steps < 3 ) steps = 3; @@ -2129,7 +2125,8 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) if( steps>1000) steps = 1000; // Protection against having vel_normal with nan values - if(len>0.0f) { + if(len>0.0f) + { direction*= 1.0f/len; } @@ -2138,7 +2135,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) for(unsigned int i = 2; i < steps; ++i ) { step_coord = m_kart->getXYZ()+direction*m_kart_length * float(i); - + QuadGraph::get()->spatialToTrackUnrolled(&step_track_coord, step_coord, m_track_node, j, future_successor_idx); @@ -2152,15 +2149,14 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) .getCenter(); return; } - } - angle = angle1; + angle = angle1; *last_node = target_sector; - } + } // If we are inside entire unrolled set then set the aim_point to be center of the last unrolled quad *aim_position = QuadGraph::get()->getNode(m_track_node). getUnrolledQuad(future_successor_idx,QuadGraph::get()->getNumberOfUnrolledQuads()).getCenter(); - + } // findNonCrashingPoint //----------------------------------------------------------------------------- diff --git a/src/tracks/graph_node.cpp b/src/tracks/graph_node.cpp index c80eb4508..0ff4cfaff 100644 --- a/src/tracks/graph_node.cpp +++ b/src/tracks/graph_node.cpp @@ -283,10 +283,9 @@ void GraphNode::buildUnrolledQuads(unsigned int unroll_quad_count) { m_unrolled_quads.clear(); - unsigned int numberOfForks = 1; GraphNode* currentNode = this; - for (int i = 0; i < unroll_quad_count+1; i++) + for (unsigned i = 0; i < unroll_quad_count+1; i++) { unsigned int successorCount = currentNode->getNumberOfSuccessors(); if (successorCount >= 2) @@ -303,7 +302,7 @@ void GraphNode::buildUnrolledQuads(unsigned int unroll_quad_count) m_unrolled_quads.resize(numberOfForks); - for (int i = 0; i < numberOfForks; i++) + for (unsigned i = 0; i < numberOfForks; i++) { Quad thisQuad = getQuad(); m_unrolled_quads[i].push_back(thisQuad); diff --git a/src/tracks/quad_graph.cpp b/src/tracks/quad_graph.cpp index f0f82f960..6fcfac3fc 100644 --- a/src/tracks/quad_graph.cpp +++ b/src/tracks/quad_graph.cpp @@ -631,16 +631,20 @@ void QuadGraph::spatialToTrack(Vec3 *dst, const Vec3& xyz, getNode(sector).getDistances(xyz, dst); } // spatialToTrack +//----------------------------------------------------------------------------- void QuadGraph::spatialToTrackUnrolled(Vec3 *dst, const Vec3& xyz, - const int parent_sector, const int unroll_qd_idx, const int fork_number) const + const int parent_sector, + const int unroll_qd_idx, + const int fork_number) const { if (parent_sector == UNKNOWN_SECTOR) { - Log::warn("Quad Graph", "UNKNOWN_SECTOR in spatialToTrack()."); + Log::warn("Quad Graph", "UNKNOWN_SECTOR in spatialToTrackUnrolled()."); return; } - getNode(parent_sector).getDistancesUnrolled(xyz, fork_number, unroll_qd_idx, dst); -} + getNode(parent_sector).getDistancesUnrolled(xyz, fork_number, + unroll_qd_idx, dst); +} // spatialToTrackUnrolled //----------------------------------------------------------------------------- /** findRoadSector returns in which sector on the road the position diff --git a/src/tracks/quad_graph.hpp b/src/tracks/quad_graph.hpp index c6a8e92f4..e27871097 100644 --- a/src/tracks/quad_graph.hpp +++ b/src/tracks/quad_graph.hpp @@ -195,7 +195,7 @@ public: { return getNode(node).getUnrolledQuad(fork_number,quad_number); } // ---------------------------------------------------------------------- /** Returns the number of forward quads that are unrolled for each quad **/ - int getNumberOfUnrolledQuads() const { return m_unroll_quad_count; } + unsigned int getNumberOfUnrolledQuads() const { return m_unroll_quad_count; } }; // QuadGraph From 3eb94e023df26395e6b3a52560d9b5b0f02159a5 Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 2 Aug 2016 21:24:10 +1000 Subject: [PATCH 102/350] Fixed error in rewind when only event but no state information was available at chosen rewind-to time. --- src/network/rewind_manager.cpp | 48 ++++++++++++++++++++++------------ src/network/rewind_manager.hpp | 2 +- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index 6a1bc020c..f3312f3ca 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -159,7 +159,7 @@ void RewindManager::insertRewindInfo(RewindInfo *ri) return; } - else // is a states + else // is a state { // If there are several infos for the same time t, // a state must be inserted first @@ -175,12 +175,13 @@ void RewindManager::insertRewindInfo(RewindInfo *ri) m_rewind_info.insert(insert_point,ri); return; } -} // insertRewindData +} // insertRewindInfo // ---------------------------------------------------------------------------- /** Returns the first (i.e. lowest) index i in m_rewind_info which fulfills - * time(i) < target_time <= time(i+1) - * This is used to determine the starting point from which to rewind. + * time(i) < target_time <= time(i+1) and is a state. This is the state + * from which a rewind can start - all states for the karts will be well + * defined. * \param time Time for which an index is searched. * \return Index in m_rewind_info after which to add rewind data. */ @@ -188,32 +189,45 @@ unsigned int RewindManager::findFirstIndex(float target_time) const { // For now do a linear search, even though m_rewind_info is sorted // I would expect that most insertions will be towards the (very) - // end of the list. Note that after finding an entry in a binary - // search, you stil have to do a linear search to find the last - // entry with the same time in order to minimise the later - // necessary memory move. + // end of the list, since rewinds should be for short periods of time. + // Note that after finding an entry in a binary search, you still + // have to do a linear search to find the last entry with the same + // time in order to minimise the later necessary memory move. // Gather some statistics about search for now: #ifdef REWIND_SEARCH_STATS m_count_of_searches++; #endif int index = m_rewind_info.size()-1; + int index_last_state = -1; while(index>=0) { #ifdef REWIND_SEARCH_STATS m_count_of_comparisons++; #endif - if(m_rewind_info[index]->getTime()isTime() ) - return index; + if(m_rewind_info[index]->isState()) + { + if(m_rewind_info[index]->getTime()getTime(), target_time); - return 0; // avoid compiler warning + if(index_last_state<0) + { + Log::fatal("RewindManager", + "Can't find any state when rewinding to %f - aborting.", + target_time); + } + + // Otherwise use the last found state - not much we can do in this case. + Log::error("RewindManager", + "Can't find state to rewind to for time %f, using %f.", + target_time, m_rewind_info[index_last_state]->getTime()); + return index_last_state; // avoid compiler warning } // findFirstIndex // ---------------------------------------------------------------------------- @@ -244,6 +258,8 @@ void RewindManager::saveStates() float time = World::getWorld()->getTime(); if(time - m_last_saved_state < m_state_frequency) { + // No full state necessary, add a dummy entry for the time + // which increases replay precision (same time step size) RewindInfo *ri = new RewindInfo(); insertRewindInfo(ri); return; diff --git a/src/network/rewind_manager.hpp b/src/network/rewind_manager.hpp index 195ed0c84..6b747bbd9 100644 --- a/src/network/rewind_manager.hpp +++ b/src/network/rewind_manager.hpp @@ -87,7 +87,7 @@ private: // ======================================================================== /** Used to store rewind information for a given time for all rewind - * instances. + * instances. * Rewind information can either be a state (for example a kart would * have position, rotation, linear and angular velocity, ... as state), * or an event (for a kart that would be pressing or releasing of a key). From 1025e258464e44cf6710ccb1f91e32391ba70651 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 3 Aug 2016 17:48:38 +1000 Subject: [PATCH 103/350] Refactored RewindInfo into astand-alone class/file with separate classes for time, event, and states. --- sources.cmake | 2 +- src/network/rewind_info.cpp | 55 +++++++++ src/network/rewind_info.hpp | 199 +++++++++++++++++++++++++++++++++ src/network/rewind_manager.cpp | 86 ++++++-------- src/network/rewind_manager.hpp | 109 +----------------- 5 files changed, 291 insertions(+), 160 deletions(-) create mode 100644 src/network/rewind_info.cpp create mode 100644 src/network/rewind_info.hpp diff --git a/sources.cmake b/sources.cmake index f484b15d5..dfc0e3774 100644 --- a/sources.cmake +++ b/sources.cmake @@ -1,5 +1,5 @@ # Modify this file to change the last-modified date when you add/remove a file. -# This will then trigger a new cmake run automatically. +# This will then trigger a new cmake run automatically. file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp") file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp") file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*") diff --git a/src/network/rewind_info.cpp b/src/network/rewind_info.cpp new file mode 100644 index 000000000..87d361c66 --- /dev/null +++ b/src/network/rewind_info.cpp @@ -0,0 +1,55 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 Joerg Henrichs +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/rewind_info.hpp" + +#include "modes/world.hpp" +#include "physics/physics.hpp" + +/** Constructor for a state: it only takes the size, and allocates a buffer + * for all state info. + * \param size Necessary buffer size for a state. + */ +RewindInfo::RewindInfo(float time, bool is_confirmed) +{ + m_time = time; + m_is_confirmed = is_confirmed; +} // RewindInfo + +// ============================================================================ +RewindInfoTime::RewindInfoTime(float time) + : RewindInfo(time, /*is_confirmed*/true) +{ +} // RewindInfoTime + +// ============================================================================ +RewindInfoState::RewindInfoState(float time, Rewinder *rewinder, char *buffer, + bool is_confirmed) + : RewindInfoRewinder(time, rewinder, buffer, is_confirmed) +{ + m_local_physics_time = World::getWorld()->getPhysics()->getPhysicsWorld() + ->getLocalTime(); +} // RewindInfoState + +// ============================================================================ +RewindInfoEvent::RewindInfoEvent(float time, Rewinder *rewinder, char *buffer, + bool is_confirmed) + : RewindInfoRewinder(time, rewinder, buffer, is_confirmed) +{ +} // RewindInfoEvent + diff --git a/src/network/rewind_info.hpp b/src/network/rewind_info.hpp new file mode 100644 index 000000000..ecdc2d66b --- /dev/null +++ b/src/network/rewind_info.hpp @@ -0,0 +1,199 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 Joerg Henrichs +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_REWIND_INFO_HPP +#define HEADER_REWIND_INFO_HPP + +#include "network/rewinder.hpp" +#include "utils/ptr_vector.hpp" + +#include +#include + + /** Used to store rewind information for a given time for all rewind + * instances. + * Rewind information can either be a state (for example a kart would + * have position, rotation, linear and angular velocity, ... as state), + * or an event (for a kart that would be pressing or releasing of a key). + * State changes and events can be delivered in different frequencies, + * and might be released (to save memory) differently: A state can be + * reproduced from a previous state by replaying the simulation taking + * all events into account. + */ +class RewindInfo +{ +private: + /** Time when this state was taken. */ + float m_time; + + /** A confirmed event is one that was sent from the server. When + * rewinding we have to start with a confirmed state for each + * object. */ + bool m_is_confirmed; + +public: + RewindInfo(float time, bool is_confirmed); + + /** Called when going back in time to undo any rewind information. */ + virtual void undo() = 0; + + /** This is called while going forwards in time again to reach current + * time. */ + virtual void rewind() = 0; + + // ------------------------------------------------------------------------ + virtual ~RewindInfo() { } + // ------------------------------------------------------------------------ + /** Returns the time at which this rewind state was saved. */ + float getTime() const { return m_time; } + // ------------------------------------------------------------------------ + /** Returns if this state is confirmed. */ + bool isConfirmed() const { return m_is_confirmed; } + // ------------------------------------------------------------------------ + /** If this rewind info is an event. Subclasses will overwrite this. */ + virtual bool isEvent() const { return false; } + // ------------------------------------------------------------------------ + /** If this rewind info is time info. Subclasses will overwrite this. */ + virtual bool isTime() const { return false; } + // ------------------------------------------------------------------------ + /** If this rewind info is an event. Subclasses will overwrite this. */ + virtual bool isState() const { return false; } + // ------------------------------------------------------------------------ +}; // RewindInfo + +// ============================================================================ +/** A rewind info abstract class that keeps track of a rewinder object. + */ +class RewindInfoRewinder : public RewindInfo +{ +protected: + /** The Rewinder instance for which this data is. */ + Rewinder *m_rewinder; + + /** Pointer to the buffer which stores all states. */ + char *m_buffer; + +public: + RewindInfoRewinder(float time, Rewinder *rewinder, char *buffer, + bool is_confirmed) + : RewindInfo(time, is_confirmed) + { + m_rewinder = rewinder; + m_buffer = buffer; + } // RewindInfoRewinder + // ------------------------------------------------------------------------ + ~RewindInfoRewinder() + { + } // ~RewindInfoRewinder + +}; // RewindInfoRewinder + +// ============================================================================ +class RewindInfoTime : public RewindInfo +{ +private: + +public: + RewindInfoTime(float time); + virtual ~RewindInfoTime() {}; + + // ------------------------------------------------------------------------ + virtual bool isTime() const { return true; } + // ------------------------------------------------------------------------ + /** Called when going back in time to undo any rewind information. + * Does actually nothing. */ + virtual void undo() {} + // ------------------------------------------------------------------------ + /** Rewinds to this state. Nothing to be done for time info. */ + virtual void rewind() {} +}; // class RewindInfoTime + +// ============================================================================ +class RewindInfoState: public RewindInfoRewinder +{ +private: + /** The 'left over' time from the physics. */ + float m_local_physics_time; + +public: + RewindInfoState(float time, Rewinder *rewinder, char *buffer, + bool is_confirmed); + virtual ~RewindInfoState() {}; + + // ------------------------------------------------------------------------ + /** Returns the left-over physics time. */ + float getLocalPhysicsTime() const { return m_local_physics_time; } + // ------------------------------------------------------------------------ + /** Returns a pointer to the state buffer. */ + char *getBuffer() const { return m_buffer; } + // ------------------------------------------------------------------------ + virtual bool isState() const { return true; } + // ------------------------------------------------------------------------ + /** Called when going back in time to undo any rewind information. + * It calls undoState in the rewinder. */ + virtual void undo() + { + m_rewinder->undoState(m_buffer); + } // undoEvent + // ------------------------------------------------------------------------ + /** Rewinds to this state. This is called while going forwards in time + * again to reach current time. It will call rewindToState(char *) + * if the state is a confirmed state. */ + virtual void rewind() + { + if (isConfirmed()) + m_rewinder->rewindToState(m_buffer); + else + { + // TODO + // Handle replacing of stored states. + } + } // rewind +}; // class RewindInfoState + +// ============================================================================ +class RewindInfoEvent : public RewindInfoRewinder +{ +public: + RewindInfoEvent(float time, Rewinder *rewinder, char *buffer, + bool is_confirmed); + virtual ~RewindInfoEvent() {} + + // ------------------------------------------------------------------------ + /** Returns a pointer to the state buffer. */ + char *getBuffer() const { return m_buffer; } + // ------------------------------------------------------------------------ + virtual bool isEvent() const { return true; } + // ------------------------------------------------------------------------ + /** Called when going back in time to undo any rewind information. + * It calls undoEvent in the rewinder. */ + virtual void undo() + { + m_rewinder->undoEvent(m_buffer); + } // undo + // ------------------------------------------------------------------------ + /** This is called while going forwards in time again to reach current + * time. Calls rewindEvent(char*). + */ + virtual void rewind() + { + m_rewinder->rewindToEvent(m_buffer); + } // rewind +}; // class RewindIndoEvent + +#endif diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index f3312f3ca..501b755e6 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -21,6 +21,7 @@ #include "graphics/irr_driver.hpp" #include "modes/world.hpp" #include "network/rewinder.hpp" +#include "network/rewind_info.hpp" #include "physics/physics.hpp" #include "race/history.hpp" #include "utils/log.hpp" @@ -45,35 +46,6 @@ void RewindManager::destroy() m_rewind_manager = NULL; } // destroy -// ============================================================================ -/** Constructor for a state: it only takes the size, and allocates a buffer - * for all state info. - * \param size Necessary buffer size for a state. - */ -RewindManager::RewindInfo::RewindInfo(Rewinder *rewinder, char *buffer, - bool is_event, bool is_confirmed) -{ - m_rewinder = rewinder; - m_time = RewindManager::get()->getCurrentTime(); - m_time_step = RewindManager::get()->getCurrentTimeStep(); - m_local_physics_time = World::getWorld()->getPhysics()->getPhysicsWorld()->getLocalTime(); - m_buffer = buffer; - m_type = is_event ? RIT_EVENT : RIT_STATE; - m_is_confirmed = is_confirmed; -} // RewindInfo - -// ---------------------------------------------------------------------------- -RewindManager::RewindInfo::RewindInfo() -{ - m_rewinder = NULL; - m_time = RewindManager::get()->getCurrentTime(); - m_time_step = RewindManager::get()->getCurrentTimeStep(); - m_local_physics_time = World::getWorld()->getPhysics()->getPhysicsWorld()->getLocalTime(); - m_buffer = NULL; - m_type = RIT_TIME; - m_is_confirmed = true; -} // RewindInfo - // ============================================================================ /** The constructor. */ @@ -239,8 +211,8 @@ unsigned int RewindManager::findFirstIndex(float target_time) const void RewindManager::addEvent(Rewinder *rewinder, char *buffer) { if(m_is_rewinding) return; - RewindInfo *ri = new RewindInfo(rewinder, buffer, /*is_event*/true, - /*is_confirmed*/true); + RewindInfo *ri = new RewindInfoEvent(getCurrentTime(), rewinder, + buffer, /*is_confirmed*/true ); insertRewindInfo(ri); } // addEvent @@ -260,7 +232,7 @@ void RewindManager::saveStates() { // No full state necessary, add a dummy entry for the time // which increases replay precision (same time step size) - RewindInfo *ri = new RewindInfo(); + RewindInfo *ri = new RewindInfoTime(getCurrentTime()); insertRewindInfo(ri); return; } @@ -273,9 +245,9 @@ void RewindManager::saveStates() if(size>=0) { m_overall_state_size += size; - RewindInfo *ri = new RewindInfo(m_all_rewinder[i], p, - /*is_event*/false, - /*is_confirmed*/true); + RewindInfo *ri = new RewindInfoState(getCurrentTime(), + m_all_rewinder[i], p, + /*is_confirmed*/true); assert(ri); insertRewindInfo(ri); } // size >= 0 @@ -302,18 +274,18 @@ void RewindManager::rewindTo(float rewind_time) // First find the state to which we need to rewind // ------------------------------------------------ - int state = findFirstIndex(rewind_time); + int state_index = findFirstIndex(rewind_time); - if(!m_rewind_info[state]->isState()) + if(!m_rewind_info[state_index]->isState()) { Log::error("RewindManager", "No state for rewind to %f, state %d.", - rewind_time, state); + rewind_time, state_index); return; } // Then undo the states that are skipped // ------------------------------------- - for(int i=m_rewind_info.size()-1; i>(int)state; i--) + for(int i=m_rewind_info.size()-1; i>(int)state_index; i--) { m_rewind_info[i]->undo(); } // for i>state @@ -335,8 +307,11 @@ void RewindManager::rewindTo(float rewind_time) // Rewind to the required state // ---------------------------- - float exact_rewind_time = m_rewind_info[state]->getTime(); - float local_physics_time = m_rewind_info[state]->getLocalPhysicsTime(); + + RewindInfoState *state = + dynamic_cast(m_rewind_info[state_index]); + float exact_rewind_time = state->getTime(); + float local_physics_time = state->getLocalPhysicsTime(); World *world = World::getWorld(); world->getPhysics()->getPhysicsWorld()->setLocalTime(local_physics_time); @@ -353,22 +328,21 @@ void RewindManager::rewindTo(float rewind_time) // Now go forward through the saved states, and // replay taking the events into account. while( world->getTime() < current_time && - state < (int)m_rewind_info.size() ) + state_index < (int)m_rewind_info.size() ) { - float dt = determineTimeStepSize(state, current_time); - // Now set all events and confirmed states - while(state < (int)m_rewind_info.size() && - m_rewind_info[state]->getTime()<=world->getTime()+0.001f) + while(state_index < (int)m_rewind_info.size() && + m_rewind_info[state_index]->getTime()<=world->getTime()+0.001f) { - if(m_rewind_info[state]->isEvent() || - m_rewind_info[state]->isConfirmed() ) + if(m_rewind_info[state_index]->isEvent() || + m_rewind_info[state_index]->isConfirmed() ) { - m_rewind_info[state]->rewind(); + m_rewind_info[state_index]->rewind(); } - state++; + state_index++; } + float dt = determineTimeStepSize(state_index, current_time); world->updateWorld(dt); #define SHOW_ROLLBACK #ifdef SHOW_ROLLBACK @@ -391,9 +365,17 @@ void RewindManager::rewindTo(float rewind_time) */ float RewindManager::determineTimeStepSize(int next_state, float end_time) { - return m_rewind_info[next_state]->getTimeStep(); + // If there is a next state (which is known to have a different time) + // use the time difference to determine the time step size. + if(next_state < m_rewind_info.size()) + return m_rewind_info[next_state]->getTime() - World::getWorld()->getTime(); + + // Otherwise, i.e. we are rewinding the last state/event, take the + // difference between that time and the world time at which the rewind + // was triggered. + return end_time - m_rewind_info[next_state-1]->getTime(); + - return m_rewind_info[next_state]->getTime()-World::getWorld()->getTime(); float dt = 1.0f/60.0f; float t = World::getWorld()->getTime(); diff --git a/src/network/rewind_manager.hpp b/src/network/rewind_manager.hpp index 6b747bbd9..3d93c3709 100644 --- a/src/network/rewind_manager.hpp +++ b/src/network/rewind_manager.hpp @@ -25,6 +25,8 @@ #include #include +class RewindInfo; + /** \ingroup network * This class manages rewinding. It keeps track of: * - states for each rewindable object (for example a kart would have @@ -85,113 +87,6 @@ private: /** A list of all objects that can be rewound. */ AllRewinder m_all_rewinder; - // ======================================================================== - /** Used to store rewind information for a given time for all rewind - * instances. - * Rewind information can either be a state (for example a kart would - * have position, rotation, linear and angular velocity, ... as state), - * or an event (for a kart that would be pressing or releasing of a key). - * State changes and events can be delivered in different frequencies, - * and might be released (to save memory) differently: A state can be - * reproduced from a previous state by replaying the simulation taking - * all events into account. - */ - class RewindInfo - { - private: - /** The different information types that can be saved. */ - enum RewindInfoType {RIT_TIME, RIT_STATE, RIT_EVENT}; - - /** Pointer to the buffer which stores all states. */ - char *m_buffer; - - /** Time when this state was taken. */ - float m_time; - - /** Time step size. */ - float m_time_step; - - /** The 'left over' time from the physics. */ - float m_local_physics_time; - - /** Type of this information. */ - RewindInfoType m_type; - - /** A confirmed event is one that was sent from the server. When - * rewinding we have to start with a confirmed state for each - * object. */ - bool m_is_confirmed; - - /** The Rewinder instance for which this data is. */ - Rewinder *m_rewinder; - public: - RewindInfo(Rewinder *rewinder, char *buffer, - bool is_event, bool is_confirmed); - // -------------------------------------------------------------------- - RewindInfo(); - // -------------------------------------------------------------------- - ~RewindInfo() - { - delete m_buffer; - } // ~RewindInfo - // -------------------------------------------------------------------- - /** Returns a pointer to the state buffer. */ - char *getBuffer() const { return m_buffer; } - // -------------------------------------------------------------------- - /** Returns the time at which this rewind state was saved. */ - float getTime() const { return m_time; } - // -------------------------------------------------------------------- - /** Time step size. */ - float getTimeStep() const { return m_time_step; } - // -------------------------------------------------------------------- - bool isEvent() const { return m_type==RIT_EVENT; } - // -------------------------------------------------------------------- - bool isTime() const { return m_type==RIT_TIME; } - // -------------------------------------------------------------------- - bool isState() const { return m_type==RIT_STATE; } - // -------------------------------------------------------------------- - /** Returns if this state is confirmed. */ - bool isConfirmed() const { return m_is_confirmed; } - // -------------------------------------------------------------------- - /** Returns the left-over physics time. */ - float getLocalPhysicsTime() const { return m_local_physics_time; } - // -------------------------------------------------------------------- - /** Called when going back in time to undo any rewind information. - * It calls either undoEvent or undoState in the rewinder. */ - void undo() - { - if(m_type==RIT_EVENT) - m_rewinder->undoEvent(m_buffer); - else if(m_type==RIT_STATE) - m_rewinder->undoState(m_buffer); - // time evnet can be ignored. - } // undoEvent - // -------------------------------------------------------------------- - /** Rewinds to this state. This is called while going forwards in time - * again to reach current time. If the info is a state, it will - * call rewindToState(char *) if the state is a confirmed state, or - * rewindReplace(char*) in order to discard the old stored data, - * and replace it with the new state at that time. In case of an - * event, rewindEvent(char*) is called. - */ - void rewind() - { - if(m_type==RIT_EVENT) - m_rewinder->rewindToEvent(m_buffer); - else if(m_type==RIT_STATE) - { - if(m_is_confirmed) - m_rewinder->rewindToState(m_buffer); - else - { - // TODO - // Handle replacing of stored states. - } - } // time information can be ignored - } // rewind - }; // RewindInfo - // ======================================================================== - /** Pointer to all saved states. */ typedef std::vector AllRewindInfo; From 05db7c14819d9a637d173d096ae6e106ddd9f094 Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 4 Aug 2016 23:15:12 +1000 Subject: [PATCH 104/350] Use BareNetworkString for storing states and events. --- src/karts/controller/kart_control.hpp | 19 +++---- src/karts/kart_rewinder.cpp | 62 +++++++++++++---------- src/karts/kart_rewinder.hpp | 11 ++-- src/network/network_string.hpp | 3 ++ src/network/rewind_info.cpp | 8 +-- src/network/rewind_info.hpp | 72 ++++++++++++++------------- src/network/rewind_manager.cpp | 17 ++++--- src/network/rewind_manager.hpp | 2 +- src/network/rewinder.hpp | 12 +++-- 9 files changed, 114 insertions(+), 92 deletions(-) diff --git a/src/karts/controller/kart_control.hpp b/src/karts/controller/kart_control.hpp index d947b4ae8..95045e62a 100644 --- a/src/karts/controller/kart_control.hpp +++ b/src/karts/controller/kart_control.hpp @@ -19,6 +19,7 @@ #ifndef HEADER_KART_CONTROL_HPP #define HEADER_KART_CONTROL_HPP +#include "network/network_string.hpp" #include @@ -85,20 +86,20 @@ public: static int getLength() { return 9; } // ------------------------------------------------------------------------ /** Copies the important data from this objects into a memory buffer. */ - void copyToMemory(char *buffer) + void copyToBuffer(BareNetworkString *buffer) { - memcpy(buffer, &m_steer, sizeof(float)); - memcpy(buffer+sizeof(float), &m_accel, sizeof(float)); - buffer[2*sizeof(float)] = getButtonsCompressed(); - } // copyToMemory + buffer->add(m_steer); + buffer->add(m_accel); + buffer->addChar(getButtonsCompressed()); + } // copyToBuffer // ------------------------------------------------------------------------ /** Restores this object from a previously saved memory buffer. */ - void setFromMemory(char *buffer) + void setFromBuffer(BareNetworkString *buffer) { - memcpy(&m_steer, buffer , sizeof(float)); - memcpy(&m_accel, buffer+ sizeof(float), sizeof(float)); - setButtonsCompressed(buffer[2*sizeof(float)] ); + m_steer = buffer->getFloat(); + m_accel = buffer->getFloat(); + setButtonsCompressed(buffer->getUInt8()); } // setFromMemory // ------------------------------------------------------------------------ diff --git a/src/karts/kart_rewinder.cpp b/src/karts/kart_rewinder.cpp index 48d78594d..8424b88b8 100644 --- a/src/karts/kart_rewinder.cpp +++ b/src/karts/kart_rewinder.cpp @@ -18,8 +18,10 @@ #include "karts/kart_rewinder.hpp" #include "karts/abstract_kart.hpp" +#include "items/attachment.hpp" #include "modes/world.hpp" #include "network/rewind_manager.hpp" +#include "network/network_string.hpp" #include "utils/vec3.hpp" #include @@ -37,44 +39,49 @@ KartRewinder::KartRewinder(AbstractKart *kart) : Rewinder(/*can_be_destroyed*/ f * \param[out] buffer Address of the memory buffer. * \returns Size of allocated memory, or -1 in case of an error. */ -int KartRewinder::getState(char **buffer) const +BareNetworkString* KartRewinder::getState() const { const int MEMSIZE = 13*sizeof(float) + 9; - *buffer = new char[MEMSIZE]; - float* p = (float*)*buffer; - if(!buffer) - { - Log::error("KartRewinder", "Can not allocate %d bytes.", MEMSIZE); - return -1; - } - + BareNetworkString *buffer = new BareNetworkString(MEMSIZE); const btRigidBody *body = m_kart->getBody(); const btTransform &t = body->getWorldTransform(); + buffer->add(t.getOrigin()); btQuaternion q = t.getRotation(); - memcpy(p+ 0, t.getOrigin(), 3*sizeof(float)); - memcpy(p+ 3, &q, 4*sizeof(float)); - memcpy(p+ 7, body->getLinearVelocity(), 3*sizeof(float)); - memcpy(p+10, body->getAngularVelocity(), 3*sizeof(float)); - m_kart->getControls().copyToMemory((char*)(p+13)); - return MEMSIZE; + buffer->add(q); + buffer->add(body->getLinearVelocity()); + buffer->add(body->getAngularVelocity()); + + // Attachment + Attachment::AttachmentType atype = m_kart->getAttachment()->getType(); + //buffer->addUInt8(uint8_t(atype)); + if(atype!=Attachment::ATTACH_NOTHING) + { + //buffer->addFloat(m_kart->getAttachment()->getTimeLeft()); + } + + // Steering information + m_kart->getControls().copyToBuffer(buffer); + + return buffer; } // getState // ---------------------------------------------------------------------------- /** Actually rewind to the specified state. */ -void KartRewinder::rewindToState(char *buffer) +void KartRewinder::rewindToState(BareNetworkString *buffer) { + buffer->reset(); // make sure the buffer is read from the beginning btTransform t; - float *p = (float*)buffer; - t.setOrigin(*(btVector3*)p); - t.setRotation(*(btQuaternion*)(p+3)); + t.setOrigin(buffer->getVec3()); + t.setRotation(buffer->getQuat()); btRigidBody *body = m_kart->getBody(); body->proceedToTransform(t); - body->setLinearVelocity(*(btVector3*)(p+7)); - body->setAngularVelocity(*(btVector3*)(p+10)); - m_kart->getControls().setFromMemory((char*)(p+13)); + body->setLinearVelocity(buffer->getVec3()); + body->setAngularVelocity(buffer->getVec3()); + + m_kart->getControls().setFromBuffer(buffer); return; } // rewindToState @@ -85,21 +92,22 @@ void KartRewinder::rewindToState(char *buffer) */ void KartRewinder::update() { - if(m_kart->getControls() == m_previous_control) + if(m_kart->getControls() == m_previous_control || + RewindManager::get()->isRewinding()) return; m_previous_control = m_kart->getControls(); - char *buffer = new char[m_previous_control.getLength()]; - m_previous_control.copyToMemory(buffer); + BareNetworkString *buffer = new BareNetworkString(m_previous_control.getLength()); + m_previous_control.copyToBuffer(buffer); // The rewind manager will free the memory once it's not needed anymore RewindManager::get()->addEvent(this, buffer); } // update // ---------------------------------------------------------------------------- -void KartRewinder::rewindToEvent(char *p) +void KartRewinder::rewindToEvent(BareNetworkString *buffer) { - m_kart->getControls().setFromMemory(p); + m_kart->getControls().setFromBuffer(buffer); }; // rewindToEvent diff --git a/src/karts/kart_rewinder.hpp b/src/karts/kart_rewinder.hpp index a95e8c9cf..013aceb85 100644 --- a/src/karts/kart_rewinder.hpp +++ b/src/karts/kart_rewinder.hpp @@ -25,6 +25,7 @@ #include "utils/cpp2011.hpp" class AbstractKart; +class BareNetworkString; class KartRewinder : public Rewinder { @@ -37,17 +38,17 @@ private: public: KartRewinder(AbstractKart *kart); virtual ~KartRewinder() {}; - virtual int getState(char **buffer) const; - virtual void rewindToState(char *p) OVERRIDE; - virtual void rewindToEvent(char *p) OVERRIDE; + virtual BareNetworkString* getState() const; + virtual void rewindToState(BareNetworkString *p) OVERRIDE; + virtual void rewindToEvent(BareNetworkString *p) OVERRIDE; // ------------------------------------------------------------------------- - virtual void undoState(char *p) OVERRIDE + virtual void undoState(BareNetworkString *p) OVERRIDE { }; // undoState // ------------------------------------------------------------------------- - virtual void undoEvent(char *p) OVERRIDE + virtual void undoEvent(BareNetworkString *p) OVERRIDE { }; // undoEvent diff --git a/src/network/network_string.hpp b/src/network/network_string.hpp index 5547cdc0e..628b1ae56 100644 --- a/src/network/network_string.hpp +++ b/src/network/network_string.hpp @@ -137,6 +137,9 @@ public: memcpy(m_buffer.data(), data, len); } // BareNetworkString + // ------------------------------------------------------------------------ + /** Allows to read a buffer from the beginning again. */ + void reset() { m_current_offset = 0; } // ------------------------------------------------------------------------ BareNetworkString& encodeString(const std::string &value); BareNetworkString& encodeString(const irr::core::stringw &value); diff --git a/src/network/rewind_info.cpp b/src/network/rewind_info.cpp index 87d361c66..81668305a 100644 --- a/src/network/rewind_info.cpp +++ b/src/network/rewind_info.cpp @@ -38,8 +38,8 @@ RewindInfoTime::RewindInfoTime(float time) } // RewindInfoTime // ============================================================================ -RewindInfoState::RewindInfoState(float time, Rewinder *rewinder, char *buffer, - bool is_confirmed) +RewindInfoState::RewindInfoState(float time, Rewinder *rewinder, + BareNetworkString *buffer, bool is_confirmed) : RewindInfoRewinder(time, rewinder, buffer, is_confirmed) { m_local_physics_time = World::getWorld()->getPhysics()->getPhysicsWorld() @@ -47,8 +47,8 @@ RewindInfoState::RewindInfoState(float time, Rewinder *rewinder, char *buffer, } // RewindInfoState // ============================================================================ -RewindInfoEvent::RewindInfoEvent(float time, Rewinder *rewinder, char *buffer, - bool is_confirmed) +RewindInfoEvent::RewindInfoEvent(float time, Rewinder *rewinder, + BareNetworkString *buffer, bool is_confirmed) : RewindInfoRewinder(time, rewinder, buffer, is_confirmed) { } // RewindInfoEvent diff --git a/src/network/rewind_info.hpp b/src/network/rewind_info.hpp index ecdc2d66b..e2ff2a9ac 100644 --- a/src/network/rewind_info.hpp +++ b/src/network/rewind_info.hpp @@ -19,25 +19,30 @@ #ifndef HEADER_REWIND_INFO_HPP #define HEADER_REWIND_INFO_HPP +#include "network/network_string.hpp" #include "network/rewinder.hpp" +#include "utils/leak_check.hpp" #include "utils/ptr_vector.hpp" #include #include - /** Used to store rewind information for a given time for all rewind - * instances. - * Rewind information can either be a state (for example a kart would - * have position, rotation, linear and angular velocity, ... as state), - * or an event (for a kart that would be pressing or releasing of a key). - * State changes and events can be delivered in different frequencies, - * and might be released (to save memory) differently: A state can be - * reproduced from a previous state by replaying the simulation taking - * all events into account. - */ +/** Used to store rewind information for a given time for all rewind + * instances. + * Rewind information can either be a state (for example a kart would + * have position, rotation, linear and angular velocity, ... as state), + * or an event (for a kart that would be pressing or releasing of a key). + * State changes and events can be delivered in different frequencies, + * and might be released (to save memory) differently: A state can be + * reproduced from a previous state by replaying the simulation taking + * all events into account. + */ + class RewindInfo { private: + LEAK_CHECK(); + /** Time when this state was taken. */ float m_time; @@ -77,30 +82,35 @@ public: }; // RewindInfo // ============================================================================ -/** A rewind info abstract class that keeps track of a rewinder object. +/** A rewind info abstract class that keeps track of a rewinder object, and + * has a BareNetworkString buffer which is used to store a state or event. */ class RewindInfoRewinder : public RewindInfo { +private: + /** Pointer to the buffer which stores all states. */ + BareNetworkString *m_buffer; + protected: /** The Rewinder instance for which this data is. */ Rewinder *m_rewinder; - /** Pointer to the buffer which stores all states. */ - char *m_buffer; - public: - RewindInfoRewinder(float time, Rewinder *rewinder, char *buffer, - bool is_confirmed) + RewindInfoRewinder(float time, Rewinder *rewinder, + BareNetworkString *buffer, bool is_confirmed) : RewindInfo(time, is_confirmed) { m_rewinder = rewinder; m_buffer = buffer; } // RewindInfoRewinder // ------------------------------------------------------------------------ - ~RewindInfoRewinder() + virtual ~RewindInfoRewinder() { + delete m_buffer; } // ~RewindInfoRewinder - + // ------------------------------------------------------------------------ + /** Returns a pointer to the state buffer. */ + BareNetworkString *getBuffer() const { return m_buffer; } }; // RewindInfoRewinder // ============================================================================ @@ -131,33 +141,30 @@ private: float m_local_physics_time; public: - RewindInfoState(float time, Rewinder *rewinder, char *buffer, - bool is_confirmed); + RewindInfoState(float time, Rewinder *rewinder, + BareNetworkString *buffer, bool is_confirmed); virtual ~RewindInfoState() {}; // ------------------------------------------------------------------------ /** Returns the left-over physics time. */ float getLocalPhysicsTime() const { return m_local_physics_time; } // ------------------------------------------------------------------------ - /** Returns a pointer to the state buffer. */ - char *getBuffer() const { return m_buffer; } - // ------------------------------------------------------------------------ virtual bool isState() const { return true; } // ------------------------------------------------------------------------ /** Called when going back in time to undo any rewind information. * It calls undoState in the rewinder. */ virtual void undo() { - m_rewinder->undoState(m_buffer); + m_rewinder->undoState(getBuffer()); } // undoEvent // ------------------------------------------------------------------------ /** Rewinds to this state. This is called while going forwards in time - * again to reach current time. It will call rewindToState(char *) + * again to reach current time. It will call rewindToState(). * if the state is a confirmed state. */ virtual void rewind() { if (isConfirmed()) - m_rewinder->rewindToState(m_buffer); + m_rewinder->rewindToState(getBuffer()); else { // TODO @@ -170,13 +177,10 @@ public: class RewindInfoEvent : public RewindInfoRewinder { public: - RewindInfoEvent(float time, Rewinder *rewinder, char *buffer, - bool is_confirmed); + RewindInfoEvent(float time, Rewinder *rewinder, + BareNetworkString *buffer, bool is_confirmed); virtual ~RewindInfoEvent() {} - // ------------------------------------------------------------------------ - /** Returns a pointer to the state buffer. */ - char *getBuffer() const { return m_buffer; } // ------------------------------------------------------------------------ virtual bool isEvent() const { return true; } // ------------------------------------------------------------------------ @@ -184,15 +188,15 @@ public: * It calls undoEvent in the rewinder. */ virtual void undo() { - m_rewinder->undoEvent(m_buffer); + m_rewinder->undoEvent(getBuffer()); } // undo // ------------------------------------------------------------------------ /** This is called while going forwards in time again to reach current - * time. Calls rewindEvent(char*). + * time. Calls rewindEvent(). */ virtual void rewind() { - m_rewinder->rewindToEvent(m_buffer); + m_rewinder->rewindToEvent(getBuffer()); } // rewind }; // class RewindIndoEvent diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index 501b755e6..a26c8752e 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -20,6 +20,7 @@ #include "graphics/irr_driver.hpp" #include "modes/world.hpp" +#include "network/network_string.hpp" #include "network/rewinder.hpp" #include "network/rewind_info.hpp" #include "physics/physics.hpp" @@ -64,6 +65,7 @@ RewindManager::~RewindManager() for(unsigned int i=0; igetState(&p); - if(size>=0) + BareNetworkString *buffer = m_all_rewinder[i]->getState(); + if(buffer && buffer->size()>=0) { - m_overall_state_size += size; + m_overall_state_size += buffer->size(); RewindInfo *ri = new RewindInfoState(getCurrentTime(), - m_all_rewinder[i], p, + m_all_rewinder[i], buffer, /*is_confirmed*/true); assert(ri); insertRewindInfo(ri); } // size >= 0 + else + delete buffer; // NULL or 0 byte buffer } Log::verbose("RewindManager", "%f allocated %ld bytes search %d/%d=%f", @@ -367,7 +370,7 @@ float RewindManager::determineTimeStepSize(int next_state, float end_time) { // If there is a next state (which is known to have a different time) // use the time difference to determine the time step size. - if(next_state < m_rewind_info.size()) + if(next_state < (int)m_rewind_info.size()) return m_rewind_info[next_state]->getTime() - World::getWorld()->getTime(); // Otherwise, i.e. we are rewinding the last state/event, take the diff --git a/src/network/rewind_manager.hpp b/src/network/rewind_manager.hpp index 3d93c3709..0097830f1 100644 --- a/src/network/rewind_manager.hpp +++ b/src/network/rewind_manager.hpp @@ -172,7 +172,7 @@ public: void reset(); void saveStates(); void rewindTo(float target_time); - void addEvent(Rewinder *rewinder, char *buffer); + void addEvent(Rewinder *rewinder, BareNetworkString *buffer); // ------------------------------------------------------------------------ /** Adds a Rewinder to the list of all rewinders. * \return true If rewinding is enabled, false otherwise. diff --git a/src/network/rewinder.hpp b/src/network/rewinder.hpp index f30936181..2720c6723 100644 --- a/src/network/rewinder.hpp +++ b/src/network/rewinder.hpp @@ -19,6 +19,8 @@ #ifndef HEADER_REWINDER_HPP #define HEADER_REWINDER_HPP +class BareNetworkString; + class Rewinder { private: @@ -32,31 +34,31 @@ public: * \param[out] buffer The address of the memory buffer with the state. * \return Size of the buffer, or -1 in case of an error. */ - virtual int getState(char **buffer) const = 0; + virtual BareNetworkString* getState() const = 0; /** Called when an event needs to be undone. This is called while going * backwards for rewinding - all stored events will get an 'undo' call. * A dummy implementation is provided which just ignores this. */ - virtual void undoEvent(char *buffer) = 0; + virtual void undoEvent(BareNetworkString *buffer) = 0; /** Called when an event needs to be replayed. This is called during * rewind, i.e. when going forward in time again. */ - virtual void rewindToEvent(char *buffer) = 0; + virtual void rewindToEvent(BareNetworkString *buffer) = 0; /** Called when a state needs to be replayed. This is called during * rewind, i.e. when going forward in time again, and only for confirmed * states. */ - virtual void rewindToState(char *buffer) = 0; + virtual void rewindToState(BareNetworkString *buffer) = 0; /** Undo the effects of the given state, but do not rewind to that * state (which is done by rewindTo). This is called while going * backwards for rewinding - all stored events will get an 'undo' call. * Provided here a dummy implementation that just ignores the state. */ - virtual void undoState(char *p) = 0; + virtual void undoState(BareNetworkString *buffer) = 0; // ------------------------------------------------------------------------- /** True if this rewinder can be destroyed. Karts can not be destroyed, From e039eb59c9ce46c379003e5adf3cd19d06e733f1 Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 5 Aug 2016 17:21:17 +1000 Subject: [PATCH 105/350] Added more fields to debug output. --- src/karts/kart.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 29d6edfab..04ed42717 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1174,14 +1174,16 @@ void Kart::update(float dt) #ifdef DEBUG_TO_COMPARE_KART_PHYSICS // This information is useful when comparing kart physics, e.g. to // see top speed, acceleration (i.e. time to top speed) etc. - Log::verbose("physics", "%s t %f xyz %f %f %f %f v %f %f %f %f maxv %f", + Log::verbose("physics", "%s t %f xyz %f %f %f %f v %f %f %f %f maxv %f s %f a %f", getIdent().c_str(), World::getWorld()->getTime(), getXYZ().getX(), getXYZ().getY(), getXYZ().getZ(), getXYZ().length(), getVelocity().getX(), getVelocity().getY(), getVelocity().getZ(), getVelocity().length(), - m_max_speed->getCurrentMaxSpeed()); + m_max_speed->getCurrentMaxSpeed(), + getControls().m_steer, + getControls().m_accel); #endif // update star effect (call will do nothing if stars are not activated) From 40697ab5f7304c3bda260e73ea0722f8bf07e73f Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 5 Aug 2016 17:22:04 +1000 Subject: [PATCH 106/350] Bugfix for rewind: previously for a floating point value f add(f) would actually call add(Vec3(f)), which adds a vector (f,f,f). --- src/network/network_string.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/network/network_string.hpp b/src/network/network_string.hpp index 628b1ae56..0c521107a 100644 --- a/src/network/network_string.hpp +++ b/src/network/network_string.hpp @@ -228,6 +228,12 @@ public: } // operator+= // ------------------------------------------------------------------------ + /** Adds a floating point number */ + BareNetworkString& add(float f) + { + return addFloat(f); + } // add + // ------------------------------------------------------------------------ /** Adds the xyz components of a Vec3 to the string. */ BareNetworkString& add(const Vec3 &xyz) { From b3f932a0e36aec5e9fc80640fc1e42ed30eb2835 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 8 Aug 2016 10:33:12 +1000 Subject: [PATCH 107/350] Renamed getState to saveState, and added support for saving a kart's attachmend in a state. --- src/items/attachment.cpp | 43 ++++++++++++++++++++++++++++++++++ src/items/attachment.hpp | 3 +++ src/karts/kart_rewinder.cpp | 30 ++++++++++++++---------- src/karts/kart_rewinder.hpp | 2 +- src/network/rewind_manager.cpp | 2 +- src/network/rewinder.hpp | 2 +- 6 files changed, 67 insertions(+), 15 deletions(-) diff --git a/src/items/attachment.cpp b/src/items/attachment.cpp index 10070c051..ab858d276 100644 --- a/src/items/attachment.cpp +++ b/src/items/attachment.cpp @@ -216,6 +216,49 @@ void Attachment::clear() m_kart->updateWeight(); } // clear +// ----------------------------------------------------------------------------- +void Attachment::saveState(BareNetworkString *buffer) +{ + // We use bit 7 to indicate if a previous owner is defined for a bomb + assert(ATTACH_MAX<=127); + uint8_t type = m_type | (( (m_type==ATTACH_BOMB) && (m_previous_owner!=NULL) ) + ? 0x80 : 0 ); + buffer->addUInt8(type); + if(m_type!=ATTACH_NOTHING) + { + buffer->addFloat(m_time_left); + if(m_type==ATTACH_BOMB && m_previous_owner) + buffer->addUInt8(m_previous_owner->getWorldKartId()); + // m_initial_speed is not saved, on restore state it will + // be set to the kart speed, which has already been restored + } +} // saveState + +// ----------------------------------------------------------------------------- +void Attachment::rewindTo(BareNetworkString *buffer) +{ + uint8_t type = buffer->getUInt8(); + AttachmentType new_type = AttachmentType(type & 0x7f); // mask out bit 7 + if(new_type==ATTACH_NOTHING) + { + clear(); + } + else + { + float time_left = buffer->getFloat(); + if(type== (ATTACH_BOMB | 0x80) ) + { + uint8_t kart_id = buffer->getUInt8(); + m_previous_owner = World::getWorld()->getKart(kart_id); + } + else + { + m_previous_owner = NULL; + } + set(new_type, time_left, m_previous_owner); + } // if something is attached +} // rewindTo + // ----------------------------------------------------------------------------- /** Randomly selects the new attachment. For a server process, the * attachment can be passed into this function. diff --git a/src/items/attachment.hpp b/src/items/attachment.hpp index 100654bb7..313a54f8d 100644 --- a/src/items/attachment.hpp +++ b/src/items/attachment.hpp @@ -28,6 +28,7 @@ using namespace irr; class AbstractKart; +class BareNetworkString; class Item; class SFXBase; @@ -111,6 +112,8 @@ public: void handleCollisionWithKart(AbstractKart *other); void set (AttachmentType type, float time, AbstractKart *previous_kart=NULL); + void rewindTo(BareNetworkString *buffer); + void saveState(BareNetworkString *buffer); // ------------------------------------------------------------------------ /** Sets the type of the attachment, but keeps the old time left value. */ diff --git a/src/karts/kart_rewinder.cpp b/src/karts/kart_rewinder.cpp index 8424b88b8..551082749 100644 --- a/src/karts/kart_rewinder.cpp +++ b/src/karts/kart_rewinder.cpp @@ -39,13 +39,15 @@ KartRewinder::KartRewinder(AbstractKart *kart) : Rewinder(/*can_be_destroyed*/ f * \param[out] buffer Address of the memory buffer. * \returns Size of allocated memory, or -1 in case of an error. */ -BareNetworkString* KartRewinder::getState() const +BareNetworkString* KartRewinder::saveState() const { const int MEMSIZE = 13*sizeof(float) + 9; BareNetworkString *buffer = new BareNetworkString(MEMSIZE); const btRigidBody *body = m_kart->getBody(); + // 1) Physics values: transform and velocities + // ------------------------------------------- const btTransform &t = body->getWorldTransform(); buffer->add(t.getOrigin()); btQuaternion q = t.getRotation(); @@ -53,36 +55,40 @@ BareNetworkString* KartRewinder::getState() const buffer->add(body->getLinearVelocity()); buffer->add(body->getAngularVelocity()); - // Attachment - Attachment::AttachmentType atype = m_kart->getAttachment()->getType(); - //buffer->addUInt8(uint8_t(atype)); - if(atype!=Attachment::ATTACH_NOTHING) - { - //buffer->addFloat(m_kart->getAttachment()->getTimeLeft()); - } - - // Steering information + // 2) Steering and other player controls + // ------------------------------------- m_kart->getControls().copyToBuffer(buffer); + // 3) Attachment + // ------------- + m_kart->getAttachment()->saveState(buffer); return buffer; -} // getState +} // saveState // ---------------------------------------------------------------------------- /** Actually rewind to the specified state. */ void KartRewinder::rewindToState(BareNetworkString *buffer) { buffer->reset(); // make sure the buffer is read from the beginning + + // 1) Physics values: transform and velocities + // ------------------------------------------- btTransform t; t.setOrigin(buffer->getVec3()); t.setRotation(buffer->getQuat()); btRigidBody *body = m_kart->getBody(); body->proceedToTransform(t); - body->setLinearVelocity(buffer->getVec3()); body->setAngularVelocity(buffer->getVec3()); + // 2) Steering and other controls + // ------------------------------ m_kart->getControls().setFromBuffer(buffer); + // 3) Attachment + // ------------- + m_kart->getAttachment()->rewindTo(buffer); + return; } // rewindToState diff --git a/src/karts/kart_rewinder.hpp b/src/karts/kart_rewinder.hpp index 013aceb85..84f48cae7 100644 --- a/src/karts/kart_rewinder.hpp +++ b/src/karts/kart_rewinder.hpp @@ -38,7 +38,7 @@ private: public: KartRewinder(AbstractKart *kart); virtual ~KartRewinder() {}; - virtual BareNetworkString* getState() const; + virtual BareNetworkString* saveState() const; virtual void rewindToState(BareNetworkString *p) OVERRIDE; virtual void rewindToEvent(BareNetworkString *p) OVERRIDE; diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index a26c8752e..60ce3922b 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -242,7 +242,7 @@ void RewindManager::saveStates() // For now always create a snapshot. for(unsigned int i=0; igetState(); + BareNetworkString *buffer = m_all_rewinder[i]->saveState(); if(buffer && buffer->size()>=0) { m_overall_state_size += buffer->size(); diff --git a/src/network/rewinder.hpp b/src/network/rewinder.hpp index 2720c6723..e5125028f 100644 --- a/src/network/rewinder.hpp +++ b/src/network/rewinder.hpp @@ -34,7 +34,7 @@ public: * \param[out] buffer The address of the memory buffer with the state. * \return Size of the buffer, or -1 in case of an error. */ - virtual BareNetworkString* getState() const = 0; + virtual BareNetworkString* saveState() const = 0; /** Called when an event needs to be undone. This is called while going * backwards for rewinding - all stored events will get an 'undo' call. From f341cd1440ff6f9e0bd2162f07f1982d0b533cb1 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 8 Aug 2016 17:46:52 +1000 Subject: [PATCH 108/350] Optimised rewind of attachments (avoid creating new attachments when the attachment was not changed). --- src/items/attachment.cpp | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/src/items/attachment.cpp b/src/items/attachment.cpp index ab858d276..b3ab694c3 100644 --- a/src/items/attachment.cpp +++ b/src/items/attachment.cpp @@ -239,24 +239,36 @@ void Attachment::rewindTo(BareNetworkString *buffer) { uint8_t type = buffer->getUInt8(); AttachmentType new_type = AttachmentType(type & 0x7f); // mask out bit 7 + + // If there is no attachment, clear the attachment if necessary and exit if(new_type==ATTACH_NOTHING) { - clear(); + if(m_type!=new_type) clear(); + return; + } + + float time_left = buffer->getFloat(); + + // Attaching an object can be expensive (loading new models, ...) + // so avoid doing this if there is no change in attachment type + if(new_type == m_type) + { + setTimeLeft(time_left); + return; + } + + // Now it is a new attachment: + + if (type == (ATTACH_BOMB | 0x80)) // we have previous owner information + { + uint8_t kart_id = buffer->getUInt8(); + m_previous_owner = World::getWorld()->getKart(kart_id); } else { - float time_left = buffer->getFloat(); - if(type== (ATTACH_BOMB | 0x80) ) - { - uint8_t kart_id = buffer->getUInt8(); - m_previous_owner = World::getWorld()->getKart(kart_id); - } - else - { - m_previous_owner = NULL; - } - set(new_type, time_left, m_previous_owner); - } // if something is attached + m_previous_owner = NULL; + } + set(new_type, time_left, m_previous_owner); } // rewindTo // ----------------------------------------------------------------------------- From 5da96cf9d042af97771fe9d0d9c874dc1b0cd53c Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 8 Aug 2016 17:57:15 +1000 Subject: [PATCH 109/350] Fixed documentation and usage of ATTACHMENT_NOLOKS_SWATTER (which is never an actual attachment value, even nolok has 'ATTACHMENT_SWATTER' set). --- src/items/attachment.cpp | 8 ++++++-- src/items/attachment.hpp | 2 ++ src/karts/controller/arena_ai.cpp | 20 ++++++++++---------- src/karts/controller/skidding_ai.cpp | 3 +-- src/karts/controller/test_ai.cpp | 3 +-- 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/items/attachment.cpp b/src/items/attachment.cpp index b3ab694c3..df6d7889d 100644 --- a/src/items/attachment.cpp +++ b/src/items/attachment.cpp @@ -106,7 +106,7 @@ void Attachment::set(AttachmentType type, float time, { bool was_bomb = (m_type == ATTACH_BOMB); scene::ISceneNode* bomb_scene_node = NULL; - if (was_bomb && type == ATTACH_SWATTER) //What about ATTACH_NOLOKS_SWATTER ?? + if (was_bomb && type == ATTACH_SWATTER) { // let's keep the bomb node, and create a new one for // the new attachment @@ -495,9 +495,13 @@ void Attachment::update(float dt) case ATTACH_MAX: break; case ATTACH_SWATTER: - case ATTACH_NOLOKS_SWATTER: // Everything is done in the plugin. break; + case ATTACH_NOLOKS_SWATTER: + // Should never be called, this symbols is only used as an index for + // the model, Nolok's attachment type is ATTACH_SWATTER + assert(false); + break; case ATTACH_BOMB: if (m_bomb_sound) m_bomb_sound->setPosition(m_kart->getXYZ()); diff --git a/src/items/attachment.hpp b/src/items/attachment.hpp index 313a54f8d..2d1a06149 100644 --- a/src/items/attachment.hpp +++ b/src/items/attachment.hpp @@ -58,6 +58,8 @@ public: ATTACH_BOMB, ATTACH_ANVIL, ATTACH_SWATTER, + // Note that the next symbol is only used as an index into the mesh + // array; it will NEVER be actually assigned as an attachment type ATTACH_NOLOKS_SWATTER, ATTACH_TINYTUX, ATTACH_BUBBLEGUM_SHIELD, diff --git a/src/karts/controller/arena_ai.cpp b/src/karts/controller/arena_ai.cpp index 4c6fa3d7b..849965be6 100644 --- a/src/karts/controller/arena_ai.cpp +++ b/src/karts/controller/arena_ai.cpp @@ -528,21 +528,21 @@ void ArenaAI::handleArenaItems(const float dt) { Attachment::AttachmentType type = m_kart->getAttachment()->getType(); // Don't use shield when we have a swatter. - if (type == Attachment::ATTACH_SWATTER || - type == Attachment::ATTACH_NOLOKS_SWATTER) + if (type == Attachment::ATTACH_SWATTER) break; // Check if a flyable (cake, ...) is close or a kart nearby // has a swatter attachment. If so, use bubblegum // as shield - if ((!m_kart->isShielded() && - projectile_manager->projectileIsClose(m_kart, - m_ai_properties->m_shield_incoming_radius)) || - (m_closest_kart_pos_data.distance < 15.0f && - ((m_closest_kart->getAttachment()-> - getType() == Attachment::ATTACH_SWATTER) || - (m_closest_kart->getAttachment()-> - getType() == Attachment::ATTACH_NOLOKS_SWATTER)))) + if ( (!m_kart->isShielded() && + projectile_manager->projectileIsClose(m_kart, + m_ai_properties->m_shield_incoming_radius) + ) || + (m_closest_kart_pos_data.distance < 15.0f && + m_closest_kart->getAttachment()->getType() == + Attachment::ATTACH_SWATTER + ) + ) { m_controls->m_fire = true; m_controls->m_look_back = false; diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index 5652784e7..4eb76ba52 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -1193,8 +1193,7 @@ void SkiddingAI::handleItems(const float dt) { Attachment::AttachmentType type = m_kart->getAttachment()->getType(); // Don't use shield when we have a swatter. - if( type == Attachment::ATTACH_SWATTER || - type == Attachment::ATTACH_NOLOKS_SWATTER ) + if( type == Attachment::ATTACH_SWATTER) break; // Check if a flyable (cake, ...) is close. If so, use bubblegum diff --git a/src/karts/controller/test_ai.cpp b/src/karts/controller/test_ai.cpp index e7e2dafaa..8e13e8e5a 100644 --- a/src/karts/controller/test_ai.cpp +++ b/src/karts/controller/test_ai.cpp @@ -1199,8 +1199,7 @@ void SkiddingAI::handleItems(const float dt) { Attachment::AttachmentType type = m_kart->getAttachment()->getType(); // Don't use shield when we have a swatter. - if( type == Attachment::ATTACH_SWATTER || - type == Attachment::ATTACH_NOLOKS_SWATTER ) + if( type == Attachment::ATTACH_SWATTER) break; // Check if a flyable (cake, ...) is close. If so, use bubblegum From 5f6c954c5abf45353083be5b3ba3b270cbe0a766 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 8 Aug 2016 18:10:30 +1000 Subject: [PATCH 110/350] Added documentation. --- src/items/attachment.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/items/attachment.cpp b/src/items/attachment.cpp index df6d7889d..1521efb9d 100644 --- a/src/items/attachment.cpp +++ b/src/items/attachment.cpp @@ -217,6 +217,9 @@ void Attachment::clear() } // clear // ----------------------------------------------------------------------------- +/** Saves the attachment state. Called as part of the kart saving its state. + * \param buffer The kart rewinder's state buffer. + */ void Attachment::saveState(BareNetworkString *buffer) { // We use bit 7 to indicate if a previous owner is defined for a bomb @@ -235,6 +238,9 @@ void Attachment::saveState(BareNetworkString *buffer) } // saveState // ----------------------------------------------------------------------------- +/** Called from the kart rewinder when resetting to a certain state. + * \param buffer The kart rewinder's buffer with the attachment state next. + */ void Attachment::rewindTo(BareNetworkString *buffer) { uint8_t type = buffer->getUInt8(); From a5ce27caa5c0ae638380a410b7d9935b64222630 Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 9 Aug 2016 17:16:45 +1000 Subject: [PATCH 111/350] Added events for new attachments. --- src/karts/kart.cpp | 6 +++-- src/karts/kart_rewinder.cpp | 46 +++++++++++++++++++++++++++++++------ src/karts/kart_rewinder.hpp | 11 +++++++++ 3 files changed, 54 insertions(+), 9 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 4ae9523d3..44c1bb16d 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -231,9 +231,8 @@ void Kart::init(RaceManager::KartType type) // Create the stars effect m_stars_effect = new Stars(this); + m_rewinder = new KartRewinder(this); reset(); - - m_rewinder = new KartRewinder(this); } // init // ---------------------------------------------------------------------------- @@ -412,6 +411,9 @@ void Kart::reset() if(m_controller) m_controller->reset(); + // Must be called after the controls are reset + m_rewinder->reset(); + // 3 strikes mode can hide the wheels scene::ISceneNode** wheels = getKartModel()->getWheelNodes(); if(wheels[0]) wheels[0]->setVisible(true); diff --git a/src/karts/kart_rewinder.cpp b/src/karts/kart_rewinder.cpp index 551082749..ffcc900ee 100644 --- a/src/karts/kart_rewinder.cpp +++ b/src/karts/kart_rewinder.cpp @@ -31,6 +31,15 @@ KartRewinder::KartRewinder(AbstractKart *kart) : Rewinder(/*can_be_destroyed*/ f m_kart = kart; } // KartRewinder +// ---------------------------------------------------------------------------- +/** Resets status in case of a resetart. + */ +void KartRewinder::reset() +{ + m_previous_attachment = m_kart->getAttachment()->getType(); + m_previous_control = m_kart->getControls(); +} // reset + // ---------------------------------------------------------------------------- /** Saves all state information for a kart in a memory buffer. The memory * is allocated here and the address returned. It will then be managed @@ -98,14 +107,33 @@ void KartRewinder::rewindToState(BareNetworkString *buffer) */ void KartRewinder::update() { - if(m_kart->getControls() == m_previous_control || - RewindManager::get()->isRewinding()) - return; - m_previous_control = m_kart->getControls(); + // Don't store events from a rewind + if(RewindManager::get()->isRewinding()) return; - BareNetworkString *buffer = new BareNetworkString(m_previous_control.getLength()); - m_previous_control.copyToBuffer(buffer); + // Check if an event has happened that needs to be recorded + bool control_event = !(m_kart->getControls() == m_previous_control); + bool attach_event = m_kart->getAttachment()->getType() + != m_previous_attachment; + uint8_t type = (control_event ? EVENT_CONTROL : 0) | + (attach_event ? EVENT_ATTACH : 0) ; + if(type == 0) + return; // no event + // Likely it is only a single event, so limit initial memory allocation + BareNetworkString *buffer = + new BareNetworkString(m_previous_control.getLength()); + buffer->addUInt8(type); + if(control_event) + { + m_previous_control = m_kart->getControls(); + m_previous_control.copyToBuffer(buffer); + } + if(attach_event) + { + m_kart->getAttachment()->saveState(buffer); + m_previous_attachment = m_kart->getAttachment()->getType(); + + } // The rewind manager will free the memory once it's not needed anymore RewindManager::get()->addEvent(this, buffer); } // update @@ -113,7 +141,11 @@ void KartRewinder::update() // ---------------------------------------------------------------------------- void KartRewinder::rewindToEvent(BareNetworkString *buffer) { - m_kart->getControls().setFromBuffer(buffer); + uint8_t type = buffer->getUInt8(); + if(type & EVENT_CONTROL) + m_kart->getControls().setFromBuffer(buffer); + if(type & EVENT_ATTACH) + m_kart->getAttachment()->rewindTo(buffer); }; // rewindToEvent diff --git a/src/karts/kart_rewinder.hpp b/src/karts/kart_rewinder.hpp index 84f48cae7..9f1d665dd 100644 --- a/src/karts/kart_rewinder.hpp +++ b/src/karts/kart_rewinder.hpp @@ -19,6 +19,7 @@ #ifndef HEADER_KART_REWINDER_HPP #define HEADER_KART_REWINDER_HPP +#include "items/attachment.hpp" #include "karts/controller/kart_control.hpp" #include "network/rewinder.hpp" @@ -33,12 +34,22 @@ private: /** Pointer to the original kart object. */ AbstractKart *m_kart; + /** The previous kart controls, used to detect if a new event + * needs to be created. */ KartControl m_previous_control; + /** The previous attachment type, used to detect if a new event + * needs to be created. */ + Attachment::AttachmentType m_previous_attachment; + + // Flags to indicate the different event types + enum { EVENT_CONTROL = 0x01, + EVENT_ATTACH = 0x02 }; public: KartRewinder(AbstractKart *kart); virtual ~KartRewinder() {}; virtual BareNetworkString* saveState() const; + void reset(); virtual void rewindToState(BareNetworkString *p) OVERRIDE; virtual void rewindToEvent(BareNetworkString *p) OVERRIDE; From f4f2f119438ce30a0f1df76269b35d81aecca486 Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 9 Aug 2016 17:39:20 +1000 Subject: [PATCH 112/350] Fix crash in case the same event is replayed more than once. --- src/karts/kart_rewinder.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/karts/kart_rewinder.cpp b/src/karts/kart_rewinder.cpp index ffcc900ee..cc4d1f836 100644 --- a/src/karts/kart_rewinder.cpp +++ b/src/karts/kart_rewinder.cpp @@ -141,6 +141,7 @@ void KartRewinder::update() // ---------------------------------------------------------------------------- void KartRewinder::rewindToEvent(BareNetworkString *buffer) { + buffer->reset(); uint8_t type = buffer->getUInt8(); if(type & EVENT_CONTROL) m_kart->getControls().setFromBuffer(buffer); From 7c598df8d15bbd8426e917f3c1c0a79f16b13e90 Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 9 Aug 2016 17:46:06 +1000 Subject: [PATCH 113/350] Save powerup state in kart status. --- src/items/powerup.cpp | 45 +++++++++++++++++++++++++++++++++++++ src/items/powerup.hpp | 6 ++++- src/karts/kart_rewinder.cpp | 10 ++++++++- 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/items/powerup.cpp b/src/items/powerup.cpp index 930c8c1bb..f5fadd687 100644 --- a/src/items/powerup.cpp +++ b/src/items/powerup.cpp @@ -69,6 +69,45 @@ void Powerup::reset() set( (PowerupManager::PowerupType)type, number ); } // reset +//----------------------------------------------------------------------------- +/** Save the powerup state. Called from the kart rewinder when saving the kart + * state or when a new powerup even is saved. + * \param buffer The buffer into which to save the state. + */ +void Powerup::saveState(BareNetworkString *buffer) +{ + buffer->addUInt8(uint8_t(m_type)); + if(m_type!=PowerupManager::POWERUP_NOTHING) + { + buffer->addUInt8(m_number); // number is <=255 + } +} // saveState + +//----------------------------------------------------------------------------- +/** Restore a powerup state. Called from the kart rewinder when restoring a + * state. + * \param buffer Buffer with the state of this powerup object. + */ +void Powerup::rewindTo(BareNetworkString *buffer) +{ + PowerupManager::PowerupType new_type = + PowerupManager::PowerupType(buffer->getUInt8()); + int n=0; + if(new_type==PowerupManager::POWERUP_NOTHING) + { + set(new_type, 0); + return; + } + n = buffer->getUInt8(); + if(m_type == new_type) + m_number = n; + else + { + m_number = 0; + set(new_type, n); + } +} // rewindTo + //----------------------------------------------------------------------------- /** Sets the collected items. The number of items is increased if the same * item is currently collected, otherwise replaces the existing item. It also @@ -81,9 +120,15 @@ void Powerup::set(PowerupManager::PowerupType type, int n) if (m_type==type) { m_number+=n; + // Limit to 255 (save space in network state saving) + if(m_number>255) m_number = 255; return; } m_type=type; + + // Limit to 255 (save space in network state saving) + if(n>255) n = 255; + m_number=n; if(m_sound_use != NULL) diff --git a/src/items/powerup.hpp b/src/items/powerup.hpp index aa4e117e9..bbe07ed64 100644 --- a/src/items/powerup.hpp +++ b/src/items/powerup.hpp @@ -26,6 +26,7 @@ #include "utils/random_generator.hpp" class AbstractKart; +class BareNetworkString; class Item; class SFXBase; @@ -56,9 +57,12 @@ public: void set (PowerupManager::PowerupType _type, int n=1); void reset (); Material* getIcon () const; - void adjustSound (); + void adjustSound (); void use (); void hitBonusBox (const Item &item, int newC=-1); + void saveState(BareNetworkString *buffer); + void rewindTo(BareNetworkString *buffer); + /** Returns the number of powerups. */ int getNum () const {return m_number;} diff --git a/src/karts/kart_rewinder.cpp b/src/karts/kart_rewinder.cpp index cc4d1f836..52ead37d2 100644 --- a/src/karts/kart_rewinder.cpp +++ b/src/karts/kart_rewinder.cpp @@ -19,6 +19,7 @@ #include "karts/kart_rewinder.hpp" #include "karts/abstract_kart.hpp" #include "items/attachment.hpp" +#include "items/powerup.hpp" #include "modes/world.hpp" #include "network/rewind_manager.hpp" #include "network/network_string.hpp" @@ -50,7 +51,7 @@ void KartRewinder::reset() */ BareNetworkString* KartRewinder::saveState() const { - const int MEMSIZE = 13*sizeof(float) + 9; + const int MEMSIZE = 13*sizeof(float) + 9+2; BareNetworkString *buffer = new BareNetworkString(MEMSIZE); const btRigidBody *body = m_kart->getBody(); @@ -71,6 +72,10 @@ BareNetworkString* KartRewinder::saveState() const // 3) Attachment // ------------- m_kart->getAttachment()->saveState(buffer); + + // 4) Powerup + // ---------- + m_kart->getPowerup()->saveState(buffer); return buffer; } // saveState @@ -98,6 +103,9 @@ void KartRewinder::rewindToState(BareNetworkString *buffer) // ------------- m_kart->getAttachment()->rewindTo(buffer); + // 4) Powerup + // ---------- + m_kart->getPowerup()->rewindTo(buffer); return; } // rewindToState From eaf21fe2f8a9edcb49bb65de85edc8ab0ec0d93e Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 10 Aug 2016 08:21:37 +1000 Subject: [PATCH 114/350] Replaced #include in kart.hpp to reduce dependencies; some coding style fixes. --- src/items/powerup.cpp | 66 +++++++++++++++++------------------ src/items/powerup.hpp | 2 +- src/items/powerup_manager.hpp | 15 ++++---- src/karts/kart.cpp | 1 + src/karts/kart.hpp | 2 +- 5 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/items/powerup.cpp b/src/items/powerup.cpp index f5fadd687..f2a49a3db 100644 --- a/src/items/powerup.cpp +++ b/src/items/powerup.cpp @@ -43,8 +43,8 @@ */ Powerup::Powerup(AbstractKart* kart) { - m_owner = kart; - m_sound_use = NULL; + m_kart = kart; + m_sound_use = NULL; reset(); } // Powerup @@ -192,14 +192,14 @@ Material *Powerup::getIcon() const */ void Powerup::adjustSound() { - m_sound_use->setPosition(m_owner->getXYZ()); + m_sound_use->setPosition(m_kart->getXYZ()); // in multiplayer mode, sounds are NOT positional (because we have multiple listeners) // so the sounds of all AIs are constantly heard. So reduce volume of sounds. if (race_manager->getNumLocalPlayers() > 1) { // player karts played at full volume; AI karts much dimmer - if (m_owner->getController()->isLocalPlayerController()) + if (m_kart->getController()->isLocalPlayerController()) { m_sound_use->setVolume( 1.0f ); } @@ -216,11 +216,11 @@ void Powerup::adjustSound() */ void Powerup::use() { - const KartProperties *kp = m_owner->getKartProperties(); + const KartProperties *kp = m_kart->getKartProperties(); // The player gets an achievement point for using a powerup if (m_type != PowerupManager::POWERUP_NOTHING && - m_owner->getController()->canGetAchievements() ) + m_kart->getController()->canGetAchievements() ) { PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_POWERUP_LOVER, "poweruplover"); } @@ -229,7 +229,7 @@ void Powerup::use() if (m_type != PowerupManager::POWERUP_NOTHING && m_type != PowerupManager::POWERUP_SWATTER && m_type != PowerupManager::POWERUP_ZIPPER) - m_owner->playCustomSFX(SFXManager::CUSTOM_SHOOT); + m_kart->playCustomSFX(SFXManager::CUSTOM_SHOOT); // FIXME - for some collectibles, set() is never called if(m_sound_use == NULL) @@ -244,12 +244,12 @@ void Powerup::use() switch (m_type) { case PowerupManager::POWERUP_ZIPPER: - m_owner->handleZipper(NULL, true); + m_kart->handleZipper(NULL, true); break ; case PowerupManager::POWERUP_SWITCH: { ItemManager::get()->switchItems(); - m_sound_use->setPosition(m_owner->getXYZ()); + m_sound_use->setPosition(m_kart->getXYZ()); m_sound_use->play(); break; } @@ -258,26 +258,26 @@ void Powerup::use() case PowerupManager::POWERUP_BOWLING: case PowerupManager::POWERUP_PLUNGER: if(stk_config->m_shield_restrict_weapos) - m_owner->setShieldTime(0.0f); // make weapon usage destroy the shield + m_kart->setShieldTime(0.0f); // make weapon usage destroy the shield Powerup::adjustSound(); m_sound_use->play(); - projectile_manager->newProjectile(m_owner, m_type); + projectile_manager->newProjectile(m_kart, m_type); break ; case PowerupManager::POWERUP_SWATTER: - m_owner->getAttachment() + m_kart->getAttachment() ->set(Attachment::ATTACH_SWATTER, kp->getSwatterDuration()); break; case PowerupManager::POWERUP_BUBBLEGUM: // use the bubble gum the traditional way, if the kart is looking back - if (m_owner->getControls().m_look_back) + if (m_kart->getControls().m_look_back) { Vec3 hit_point; Vec3 normal; const Material* material_hit; - Vec3 pos = m_owner->getXYZ(); + Vec3 pos = m_kart->getXYZ(); Vec3 to=pos+Vec3(0, -10000, 0); world->getTrack()->getTriangleMesh().castRay(pos, to, &hit_point, &material_hit, &normal); @@ -292,35 +292,35 @@ void Powerup::use() pos.setY(hit_point.getY()-0.05f); - ItemManager::get()->newItem(Item::ITEM_BUBBLEGUM, pos, normal, m_owner); + ItemManager::get()->newItem(Item::ITEM_BUBBLEGUM, pos, normal, m_kart); } else // if the kart is looking forward, use the bubblegum as a shield { - if(!m_owner->isShielded()) //if the previous shield had been used up. + if(!m_kart->isShielded()) //if the previous shield had been used up. { - if (m_owner->getIdent() == "nolok") + if (m_kart->getIdent() == "nolok") { - m_owner->getAttachment()->set(Attachment::ATTACH_NOLOK_BUBBLEGUM_SHIELD, + m_kart->getAttachment()->set(Attachment::ATTACH_NOLOK_BUBBLEGUM_SHIELD, kp->getBubblegumShieldDuration()); } else { - m_owner->getAttachment()->set(Attachment::ATTACH_BUBBLEGUM_SHIELD, + m_kart->getAttachment()->set(Attachment::ATTACH_BUBBLEGUM_SHIELD, kp->getBubblegumShieldDuration()); } } else // using a bubble gum while still having a shield { - if (m_owner->getIdent() == "nolok") + if (m_kart->getIdent() == "nolok") { - m_owner->getAttachment()->set(Attachment::ATTACH_NOLOK_BUBBLEGUM_SHIELD, - kp->getBubblegumShieldDuration() + m_owner->getShieldTime()); + m_kart->getAttachment()->set(Attachment::ATTACH_NOLOK_BUBBLEGUM_SHIELD, + kp->getBubblegumShieldDuration() + m_kart->getShieldTime()); } else { - m_owner->getAttachment()->set(Attachment::ATTACH_BUBBLEGUM_SHIELD, - kp->getBubblegumShieldDuration() + m_owner->getShieldTime()); + m_kart->getAttachment()->set(Attachment::ATTACH_BUBBLEGUM_SHIELD, + kp->getBubblegumShieldDuration() + m_kart->getShieldTime()); } } @@ -340,7 +340,7 @@ void Powerup::use() { AbstractKart *kart=world->getKart(i); if(kart->isEliminated() || kart->isInvulnerable()) continue; - if(kart == m_owner) continue; + if(kart == m_kart) continue; if(kart->getPosition() == 1) { kart->getAttachment()->set(Attachment::ATTACH_ANVIL, @@ -355,7 +355,7 @@ void Powerup::use() if(kart->getController()->isLocalPlayerController()) m_sound_use->setPosition(kart->getXYZ()); else - m_sound_use->setPosition(m_owner->getXYZ()); + m_sound_use->setPosition(m_kart->getXYZ()); m_sound_use->play(); break; @@ -373,13 +373,13 @@ void Powerup::use() for(unsigned int i = 0 ; i < world->getNumKarts(); ++i) { AbstractKart *kart=world->getKart(i); - if(kart->isEliminated() || kart== m_owner || kart->isInvulnerable()) continue; + if(kart->isEliminated() || kart== m_kart || kart->isInvulnerable()) continue; if(kart->isShielded()) { kart->decreaseShieldTime(); continue; } - if(m_owner->getPosition() > kart->getPosition()) + if(m_kart->getPosition() > kart->getPosition()) { kart->getAttachment()->set(Attachment::ATTACH_PARACHUTE, kp->getParachuteDurationOther()); @@ -393,8 +393,8 @@ void Powerup::use() // or the kart "throwing" the anvil? Ideally it should be both. // Meanwhile, don't play it near AI karts since they obviously // don't hear anything - if(m_owner->getController()->isLocalPlayerController()) - m_sound_use->setPosition(m_owner->getXYZ()); + if(m_kart->getController()->isLocalPlayerController()) + m_sound_use->setPosition(m_kart->getXYZ()); else if(player_kart) m_sound_use->setPosition(player_kart->getXYZ()); m_sound_use->play(); @@ -403,8 +403,8 @@ void Powerup::use() case PowerupManager::POWERUP_NOTHING: { - if(!m_owner->getKartAnimation()) - m_owner->beep(); + if(!m_kart->getKartAnimation()) + m_kart->beep(); } break; default : break; @@ -434,7 +434,7 @@ void Powerup::hitBonusBox(const Item &item, int add_info) { // Position can be -1 in case of a battle mode (which doesn't have // positions), but this case is properly handled in getRandomPowerup. - int position = m_owner->getPosition(); + int position = m_kart->getPosition(); unsigned int n=1; PowerupManager::PowerupType new_powerup; diff --git a/src/items/powerup.hpp b/src/items/powerup.hpp index bbe07ed64..a64e16b61 100644 --- a/src/items/powerup.hpp +++ b/src/items/powerup.hpp @@ -49,7 +49,7 @@ private: int m_number; /** The owner (kart) of this powerup. */ - AbstractKart* m_owner; + AbstractKart* m_kart; public: Powerup (AbstractKart* kart_); diff --git a/src/items/powerup_manager.hpp b/src/items/powerup_manager.hpp index 3d26df201..58dae6fec 100644 --- a/src/items/powerup_manager.hpp +++ b/src/items/powerup_manager.hpp @@ -19,20 +19,19 @@ #ifndef HEADER_POWERUPMANAGER_HPP #define HEADER_POWERUPMANAGER_HPP -namespace irr -{ - namespace scene { class IMesh; } -} +#include "utils/no_copy.hpp" + +#include "btBulletDynamicsCommon.h" #include #include -#include "btBulletDynamicsCommon.h" - -#include "utils/no_copy.hpp" - class Material; class XMLNode; +namespace irr +{ + namespace scene { class IMesh; } +} /** * \ingroup items diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 44c1bb16d..bbdc67d5a 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -43,6 +43,7 @@ #include "io/file_manager.hpp" #include "items/attachment.hpp" #include "items/item_manager.hpp" +#include "items/powerup.hpp" #include "items/projectile_manager.hpp" #include "karts/abstract_characteristic.hpp" #include "karts/abstract_kart_animation.hpp" diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index 332cce747..152b676c3 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -28,7 +28,7 @@ #include "LinearMath/btTransform.h" -#include "items/powerup.hpp" +#include "items/powerup_manager.hpp" // For PowerupType #include "karts/abstract_kart.hpp" #include "karts/kart_properties.hpp" #include "utils/no_copy.hpp" From e621e93f4c89eb79e8f51ed2704385932b93158a Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 10 Aug 2016 08:35:23 +1000 Subject: [PATCH 115/350] Updated comments. --- src/network/rewinder.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/network/rewinder.hpp b/src/network/rewinder.hpp index e5125028f..d8b1a8b97 100644 --- a/src/network/rewinder.hpp +++ b/src/network/rewinder.hpp @@ -38,7 +38,6 @@ public: /** Called when an event needs to be undone. This is called while going * backwards for rewinding - all stored events will get an 'undo' call. - * A dummy implementation is provided which just ignores this. */ virtual void undoEvent(BareNetworkString *buffer) = 0; @@ -56,7 +55,6 @@ public: /** Undo the effects of the given state, but do not rewind to that * state (which is done by rewindTo). This is called while going * backwards for rewinding - all stored events will get an 'undo' call. - * Provided here a dummy implementation that just ignores the state. */ virtual void undoState(BareNetworkString *buffer) = 0; From 239881ef5ea3dfb2fceec2b898bc0f59b0c82b1d Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 11 Aug 2016 17:14:09 +1000 Subject: [PATCH 116/350] Refactored event handling, so that now not only Rewinder objects can store events. This allows e.g. a 'new attachment' event to be handled entirely in the attachment class, not in the kart rewinder anymore. --- sources.cmake | 2 +- src/items/attachment.cpp | 20 +++++++++++++++++ src/items/attachment.hpp | 9 +++++++- src/items/item.hpp | 13 ++++++----- src/karts/kart_rewinder.cpp | 16 ++----------- src/karts/kart_rewinder.hpp | 5 ----- src/network/event_rewinder.cpp | 35 +++++++++++++++++++++++++++++ src/network/event_rewinder.hpp | 41 ++++++++++++++++++++++++++++++++++ src/network/rewind_info.cpp | 6 +++-- src/network/rewind_info.hpp | 24 +++++++++++++++----- src/network/rewind_manager.cpp | 14 ++++++++---- src/network/rewind_manager.hpp | 3 ++- 12 files changed, 148 insertions(+), 40 deletions(-) create mode 100644 src/network/event_rewinder.cpp create mode 100644 src/network/event_rewinder.hpp diff --git a/sources.cmake b/sources.cmake index dfc0e3774..5b3d0c21e 100644 --- a/sources.cmake +++ b/sources.cmake @@ -1,5 +1,5 @@ # Modify this file to change the last-modified date when you add/remove a file. -# This will then trigger a new cmake run automatically. +# This will then trigger a new cmake run automatically. file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp") file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp") file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*") diff --git a/src/items/attachment.cpp b/src/items/attachment.cpp index 1521efb9d..6875f61b0 100644 --- a/src/items/attachment.cpp +++ b/src/items/attachment.cpp @@ -36,6 +36,7 @@ #include "karts/kart_properties.hpp" #include "modes/three_strikes_battle.hpp" #include "modes/world.hpp" +#include "network/rewind_manager.hpp" #include "physics/triangle_mesh.hpp" #include "tracks/track.hpp" #include "physics/triangle_mesh.hpp" @@ -45,6 +46,7 @@ /** Initialises the attachment each kart has. */ Attachment::Attachment(AbstractKart* kart) + : EventRewinder() { m_type = ATTACH_NOTHING; m_time_left = 0.0; @@ -182,6 +184,15 @@ void Attachment::set(AttachmentType type, float time, m_node->setVisible(true); irr_driver->applyObjectPassShader(m_node); + + // Save event about the new attachment + RewindManager *rwm = RewindManager::get(); + if(rwm->isEnabled() && !rwm->isRewinding()) + { + BareNetworkString *buffer = new BareNetworkString(2); + saveState(buffer); + rwm->addEvent(this, buffer); + } } // set // ----------------------------------------------------------------------------- @@ -276,6 +287,15 @@ void Attachment::rewindTo(BareNetworkString *buffer) } set(new_type, time_left, m_previous_owner); } // rewindTo +// ----------------------------------------------------------------------------- +/** Called when going forwards in time during a rewind. + * \param buffer Buffer with the rewind information. + */ +void Attachment::rewind(BareNetworkString *buffer) +{ + // Event has same info as a state, so re-use the restore function + rewindTo(buffer); +} // rewind // ----------------------------------------------------------------------------- /** Randomly selects the new attachment. For a server process, the diff --git a/src/items/attachment.hpp b/src/items/attachment.hpp index 2d1a06149..4f0d5243f 100644 --- a/src/items/attachment.hpp +++ b/src/items/attachment.hpp @@ -21,6 +21,7 @@ #include "config/stk_config.hpp" #include "items/attachment_plugin.hpp" +#include "network/event_rewinder.hpp" #include "utils/no_copy.hpp" #include "utils/random_generator.hpp" @@ -45,7 +46,8 @@ class SFXBase; * a scene node). * \ingroup items */ -class Attachment: public NoCopy, public scene::IAnimationEndCallBack +class Attachment: public NoCopy, public scene::IAnimationEndCallBack, + public EventRewinder { public: // Some loop in attachment.cpp depend on ATTACH_FIRST and ATTACH_MAX. @@ -114,6 +116,7 @@ public: void handleCollisionWithKart(AbstractKart *other); void set (AttachmentType type, float time, AbstractKart *previous_kart=NULL); + virtual void rewind(BareNetworkString *buffer); void rewindTo(BareNetworkString *buffer); void saveState(BareNetworkString *buffer); @@ -142,6 +145,10 @@ public: // ------------------------------------------------------------------------ /** Implement IAnimatedMeshSceneNode */ virtual void OnAnimationEnd(scene::IAnimatedMeshSceneNode* node); + // ------------------------------------------------------------------------ + /** Nothing to undo when going back during a rewind, the full state info + * will take care of creating the right attachment. */ + virtual void undo(BareNetworkString *buffer) { } }; // Attachment #endif diff --git a/src/items/item.hpp b/src/items/item.hpp index 4ed941ad4..f96ac821d 100644 --- a/src/items/item.hpp +++ b/src/items/item.hpp @@ -25,11 +25,6 @@ * Defines the various collectibles and weapons of STK. */ -namespace irr -{ - namespace scene { class IMesh; class ISceneNode; } -} -using namespace irr; #include "utils/leak_check.hpp" #include "utils/no_copy.hpp" @@ -38,8 +33,14 @@ using namespace irr; #include class AbstractKart; -class LODNode; class Item; +class LODNode; + +namespace irr +{ + namespace scene { class IMesh; class ISceneNode; } +} +using namespace irr; // ----------------------------------------------------------------------------- diff --git a/src/karts/kart_rewinder.cpp b/src/karts/kart_rewinder.cpp index 52ead37d2..7ea2d6993 100644 --- a/src/karts/kart_rewinder.cpp +++ b/src/karts/kart_rewinder.cpp @@ -37,7 +37,6 @@ KartRewinder::KartRewinder(AbstractKart *kart) : Rewinder(/*can_be_destroyed*/ f */ void KartRewinder::reset() { - m_previous_attachment = m_kart->getAttachment()->getType(); m_previous_control = m_kart->getControls(); } // reset @@ -120,10 +119,7 @@ void KartRewinder::update() // Check if an event has happened that needs to be recorded bool control_event = !(m_kart->getControls() == m_previous_control); - bool attach_event = m_kart->getAttachment()->getType() - != m_previous_attachment; - uint8_t type = (control_event ? EVENT_CONTROL : 0) | - (attach_event ? EVENT_ATTACH : 0) ; + uint8_t type = (control_event ? EVENT_CONTROL : 0); if(type == 0) return; // no event @@ -136,14 +132,8 @@ void KartRewinder::update() m_previous_control = m_kart->getControls(); m_previous_control.copyToBuffer(buffer); } - if(attach_event) - { - m_kart->getAttachment()->saveState(buffer); - m_previous_attachment = m_kart->getAttachment()->getType(); - - } // The rewind manager will free the memory once it's not needed anymore - RewindManager::get()->addEvent(this, buffer); + //XXXXXXXXXXXXXXX RewindManager::get()->addEvent(this, buffer); } // update // ---------------------------------------------------------------------------- @@ -153,8 +143,6 @@ void KartRewinder::rewindToEvent(BareNetworkString *buffer) uint8_t type = buffer->getUInt8(); if(type & EVENT_CONTROL) m_kart->getControls().setFromBuffer(buffer); - if(type & EVENT_ATTACH) - m_kart->getAttachment()->rewindTo(buffer); }; // rewindToEvent diff --git a/src/karts/kart_rewinder.hpp b/src/karts/kart_rewinder.hpp index 9f1d665dd..adab48939 100644 --- a/src/karts/kart_rewinder.hpp +++ b/src/karts/kart_rewinder.hpp @@ -19,7 +19,6 @@ #ifndef HEADER_KART_REWINDER_HPP #define HEADER_KART_REWINDER_HPP -#include "items/attachment.hpp" #include "karts/controller/kart_control.hpp" #include "network/rewinder.hpp" @@ -38,10 +37,6 @@ private: * needs to be created. */ KartControl m_previous_control; - /** The previous attachment type, used to detect if a new event - * needs to be created. */ - Attachment::AttachmentType m_previous_attachment; - // Flags to indicate the different event types enum { EVENT_CONTROL = 0x01, EVENT_ATTACH = 0x02 }; diff --git a/src/network/event_rewinder.cpp b/src/network/event_rewinder.cpp new file mode 100644 index 000000000..5f0ea8475 --- /dev/null +++ b/src/network/event_rewinder.cpp @@ -0,0 +1,35 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 Joerg Henrichs +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/event_rewinder.hpp" + +#include "network/rewind_manager.hpp" + +/** Constructor. It will add this object to the list of all rewindable + * objects in the rewind manager. + */ +EventRewinder::EventRewinder() +{ +} // Rewinder + +// ---------------------------------------------------------------------------- +/** Destructor. + */ +EventRewinder::~EventRewinder() +{ +} // ~EventRewinder diff --git a/src/network/event_rewinder.hpp b/src/network/event_rewinder.hpp new file mode 100644 index 000000000..1df4a8edc --- /dev/null +++ b/src/network/event_rewinder.hpp @@ -0,0 +1,41 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 Joerg Henrichs +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_EVENT_REWINDER_HPP +#define HEADER_EVENT_REWINDER_HPP + +class BareNetworkString; + +class EventRewinder +{ +public: + EventRewinder(); + virtual ~EventRewinder(); + + /** Called when an event needs to be undone. This is called while going + * backwards for rewinding - all stored events will get an 'undo' call. + */ + virtual void undo(BareNetworkString *buffer) = 0; + + /** Called when an event needs to be replayed. This is called during + * rewind, i.e. when going forward in time again. + */ + virtual void rewind(BareNetworkString *buffer) = 0; +}; // EventRewinder +#endif + diff --git a/src/network/rewind_info.cpp b/src/network/rewind_info.cpp index 81668305a..4340f2457 100644 --- a/src/network/rewind_info.cpp +++ b/src/network/rewind_info.cpp @@ -47,9 +47,11 @@ RewindInfoState::RewindInfoState(float time, Rewinder *rewinder, } // RewindInfoState // ============================================================================ -RewindInfoEvent::RewindInfoEvent(float time, Rewinder *rewinder, +RewindInfoEvent::RewindInfoEvent(float time, EventRewinder *event_rewinder, BareNetworkString *buffer, bool is_confirmed) - : RewindInfoRewinder(time, rewinder, buffer, is_confirmed) + : RewindInfo(time, is_confirmed) { + m_event_rewinder = event_rewinder; + m_buffer = buffer; } // RewindInfoEvent diff --git a/src/network/rewind_info.hpp b/src/network/rewind_info.hpp index e2ff2a9ac..de4b033e3 100644 --- a/src/network/rewind_info.hpp +++ b/src/network/rewind_info.hpp @@ -19,6 +19,7 @@ #ifndef HEADER_REWIND_INFO_HPP #define HEADER_REWIND_INFO_HPP +#include "network/event_rewinder.hpp" #include "network/network_string.hpp" #include "network/rewinder.hpp" #include "utils/leak_check.hpp" @@ -156,7 +157,7 @@ public: virtual void undo() { m_rewinder->undoState(getBuffer()); - } // undoEvent + } // undo // ------------------------------------------------------------------------ /** Rewinds to this state. This is called while going forwards in time * again to reach current time. It will call rewindToState(). @@ -174,10 +175,16 @@ public: }; // class RewindInfoState // ============================================================================ -class RewindInfoEvent : public RewindInfoRewinder +class RewindInfoEvent : public RewindInfo { +private: + /** Pointer to the event rewinder responsible for this event. */ + EventRewinder *m_event_rewinder; + + /** Buffer with the event data. */ + BareNetworkString *m_buffer; public: - RewindInfoEvent(float time, Rewinder *rewinder, + RewindInfoEvent(float time, EventRewinder *event_rewinder, BareNetworkString *buffer, bool is_confirmed); virtual ~RewindInfoEvent() {} @@ -185,10 +192,10 @@ public: virtual bool isEvent() const { return true; } // ------------------------------------------------------------------------ /** Called when going back in time to undo any rewind information. - * It calls undoEvent in the rewinder. */ + * It calls undoEvent in the rewinder. */ virtual void undo() { - m_rewinder->undoEvent(getBuffer()); + m_event_rewinder->undo(m_buffer); } // undo // ------------------------------------------------------------------------ /** This is called while going forwards in time again to reach current @@ -196,8 +203,13 @@ public: */ virtual void rewind() { - m_rewinder->rewindToEvent(getBuffer()); + // Make sure to reset the buffer so we read from the beginning + m_buffer->reset(); + m_event_rewinder->rewind(getBuffer()); } // rewind + // ------------------------------------------------------------------------ + /** Returns the buffer with the event information in it. */ + BareNetworkString *getBuffer() { return m_buffer; } }; // class RewindIndoEvent #endif diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index 60ce3922b..befd0b694 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -210,11 +210,17 @@ unsigned int RewindManager::findFirstIndex(float target_time) const * \param time Time at which the event was recorded. * \param buffer Pointer to the event data. */ -void RewindManager::addEvent(Rewinder *rewinder, BareNetworkString *buffer) +void RewindManager::addEvent(EventRewinder *event_rewinder, + BareNetworkString *buffer) { - if(m_is_rewinding) return; - RewindInfo *ri = new RewindInfoEvent(getCurrentTime(), rewinder, - buffer, /*is_confirmed*/true ); + if(m_is_rewinding) + { + delete buffer; + Log::error("RewindManager", "Adding event when rewinding"); + return; + } + RewindInfo *ri = new RewindInfoEvent(getCurrentTime(), event_rewinder, + buffer, /*is confirmed*/true); insertRewindInfo(ri); } // addEvent diff --git a/src/network/rewind_manager.hpp b/src/network/rewind_manager.hpp index 0097830f1..653072e47 100644 --- a/src/network/rewind_manager.hpp +++ b/src/network/rewind_manager.hpp @@ -26,6 +26,7 @@ #include class RewindInfo; +class EventRewinder; /** \ingroup network * This class manages rewinding. It keeps track of: @@ -172,7 +173,7 @@ public: void reset(); void saveStates(); void rewindTo(float target_time); - void addEvent(Rewinder *rewinder, BareNetworkString *buffer); + void addEvent(EventRewinder *event_rewinder, BareNetworkString *buffer); // ------------------------------------------------------------------------ /** Adds a Rewinder to the list of all rewinders. * \return true If rewinding is enabled, false otherwise. From 7c899c943cbe5736a012476f2828d932484c9085 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 15 Aug 2016 17:26:07 +1000 Subject: [PATCH 117/350] Refactored KartControl to use setters/getters instead of just public variables. --- src/items/bowling.cpp | 2 +- src/items/cake.cpp | 2 +- src/items/plunger.cpp | 2 +- src/items/powerup.cpp | 2 +- src/karts/abstract_kart.hpp | 2 +- src/karts/controller/ai_base_controller.cpp | 16 +- src/karts/controller/arena_ai.cpp | 54 +++--- src/karts/controller/end_controller.cpp | 12 +- src/karts/controller/ghost_controller.cpp | 4 +- src/karts/controller/kart_control.hpp | 66 +++++++- .../controller/local_player_controller.cpp | 5 +- src/karts/controller/player_controller.cpp | 106 ++++++------ src/karts/controller/skidding_ai.cpp | 156 ++++++++--------- src/karts/controller/soccer_ai.cpp | 4 +- src/karts/controller/test_ai.cpp | 160 +++++++++--------- src/karts/kart.cpp | 40 ++--- src/karts/kart_gfx.cpp | 2 +- src/karts/kart_with_stats.cpp | 8 +- src/karts/skidding.cpp | 2 +- .../protocols/controller_events_protocol.cpp | 28 +-- src/race/history.cpp | 12 +- src/states_screens/race_gui.cpp | 2 +- 22 files changed, 372 insertions(+), 315 deletions(-) diff --git a/src/items/bowling.cpp b/src/items/bowling.cpp index 613a95b12..ce21c34a7 100644 --- a/src/items/bowling.cpp +++ b/src/items/bowling.cpp @@ -40,7 +40,7 @@ Bowling::Bowling(AbstractKart *kart) float y_offset = 0.5f*kart->getKartLength() + m_extend.getZ()*0.5f; // if the kart is looking backwards, release from the back - if( kart->getControls().m_look_back ) + if( kart->getControls().getLookBack()) { y_offset = -y_offset; m_speed = -m_speed*2; diff --git a/src/items/cake.cpp b/src/items/cake.cpp index aa1ce12ba..609769e70 100644 --- a/src/items/cake.cpp +++ b/src/items/cake.cpp @@ -61,7 +61,7 @@ Cake::Cake (AbstractKart *kart) : Flyable(kart, PowerupManager::POWERUP_CAKE) float pitch = kart->getTerrainPitch(heading); // Find closest kart in front of the current one - const bool backwards = kart->getControls().m_look_back; + const bool backwards = kart->getControls().getLookBack(); const AbstractKart *closest_kart=NULL; Vec3 direction; float kart_dist_squared; diff --git a/src/items/plunger.cpp b/src/items/plunger.cpp index 6f561682f..5f4dde496 100644 --- a/src/items/plunger.cpp +++ b/src/items/plunger.cpp @@ -46,7 +46,7 @@ Plunger::Plunger(AbstractKart *kart) float plunger_speed = 2 * m_speed; // if the kart is looking backwards, release from the back - m_reverse_mode = kart->getControls().m_look_back; + m_reverse_mode = kart->getControls().getLookBack(); // find closest kart in front of the current one const AbstractKart *closest_kart=0; diff --git a/src/items/powerup.cpp b/src/items/powerup.cpp index f2a49a3db..14e026255 100644 --- a/src/items/powerup.cpp +++ b/src/items/powerup.cpp @@ -272,7 +272,7 @@ void Powerup::use() case PowerupManager::POWERUP_BUBBLEGUM: // use the bubble gum the traditional way, if the kart is looking back - if (m_kart->getControls().m_look_back) + if (m_kart->getControls().getLookBack()) { Vec3 hit_point; Vec3 normal; diff --git a/src/karts/abstract_kart.hpp b/src/karts/abstract_kart.hpp index f83c01dbb..f7105603a 100644 --- a/src/karts/abstract_kart.hpp +++ b/src/karts/abstract_kart.hpp @@ -109,7 +109,7 @@ public: // Functions related to controlling the kart // ------------------------------------------------------------------------ /** Returns the current steering value for this kart. */ - float getSteerPercent() const { return m_controls.m_steer; } + float getSteerPercent() const { return m_controls.getSteer(); } // ------------------------------------------------------------------------ /** Returns all controls of this kart. */ KartControl& getControls() { return m_controls; } diff --git a/src/karts/controller/ai_base_controller.cpp b/src/karts/controller/ai_base_controller.cpp index ce8307808..09b3888de 100644 --- a/src/karts/controller/ai_base_controller.cpp +++ b/src/karts/controller/ai_base_controller.cpp @@ -186,11 +186,11 @@ void AIBaseController::setSteering(float angle, float dt) { float steer_fraction = angle / m_kart->getMaxSteerAngle(); if(!canSkid(steer_fraction)) - m_controls->m_skid = KartControl::SC_NONE; + m_controls->setSkidControl(KartControl::SC_NONE); else - m_controls->m_skid = steer_fraction > 0 ? KartControl::SC_RIGHT - : KartControl::SC_LEFT; - float old_steer = m_controls->m_steer; + m_controls->setSkidControl(steer_fraction > 0 ? KartControl::SC_RIGHT + : KartControl::SC_LEFT ); + float old_steer = m_controls->getSteer(); if (steer_fraction > 1.0f) steer_fraction = 1.0f; else if(steer_fraction < -1.0f) steer_fraction = -1.0f; @@ -205,13 +205,13 @@ void AIBaseController::setSteering(float angle, float dt) float max_steer_change = dt/m_ai_properties->m_time_full_steer; if(old_steer < steer_fraction) { - m_controls->m_steer = (old_steer+max_steer_change > steer_fraction) - ? steer_fraction : old_steer+max_steer_change; + m_controls->setSteer(( old_steer+max_steer_change > steer_fraction) + ? steer_fraction : old_steer+max_steer_change); } else { - m_controls->m_steer = (old_steer-max_steer_change < steer_fraction) - ? steer_fraction : old_steer-max_steer_change; + m_controls->setSteer( (old_steer-max_steer_change < steer_fraction) + ? steer_fraction : old_steer-max_steer_change ); } } // setSteering diff --git a/src/karts/controller/arena_ai.cpp b/src/karts/controller/arena_ai.cpp index 849965be6..fef4696ff 100644 --- a/src/karts/controller/arena_ai.cpp +++ b/src/karts/controller/arena_ai.cpp @@ -86,8 +86,8 @@ void ArenaAI::reset() void ArenaAI::update(float dt) { // This is used to enable firing an item backwards. - m_controls->m_look_back = false; - m_controls->m_nitro = false; + m_controls->setLookBack(false); + m_controls->setNitro(false); m_avoiding_banana = false; // Don't do anything if there is currently a kart animations shown. @@ -114,7 +114,7 @@ void ArenaAI::update(float dt) if (m_kart->getSpeed() > 15.0f && m_turn_angle < 20) { // Only use nitro when turn angle is big (180 - angle) - m_controls->m_nitro = true; + m_controls->setNitro(true); } if (m_is_uturn) @@ -298,15 +298,15 @@ void ArenaAI::checkIfStuck(const float dt) void ArenaAI::handleArenaAcceleration(const float dt) { - if (m_controls->m_brake) + if (m_controls->getBrake()) { - m_controls->m_accel = 0.0f; + m_controls->setAccel(0.0f); return; } const float handicap = (m_cur_difficulty == RaceManager::DIFFICULTY_EASY ? 0.7f : 1.0f); - m_controls->m_accel = stk_config->m_ai_acceleration * handicap; + m_controls->setAccel(stk_config->m_ai_acceleration * handicap); } // handleArenaAcceleration @@ -318,9 +318,9 @@ void ArenaAI::handleArenaUTurn(const float dt) if (fabsf(m_kart->getSpeed()) > (m_kart->getKartProperties()->getEngineMaxSpeed() / 5) && m_kart->getSpeed() < 0) // Try to emulate reverse like human players - m_controls->m_accel = -0.06f; + m_controls->setAccel(-0.06f); else - m_controls->m_accel = -5.0f; + m_controls->setAccel(-5.0f); if (m_time_since_uturn >= (m_cur_difficulty == RaceManager::DIFFICULTY_EASY ? 2.0f : 1.5f)) @@ -351,9 +351,9 @@ bool ArenaAI::handleArenaUnstuck(const float dt) if (fabsf(m_kart->getSpeed()) > (m_kart->getKartProperties()->getEngineMaxSpeed() / 5) && m_kart->getSpeed() < 0) - m_controls->m_accel = -0.06f; + m_controls->setAccel(-0.06f); else - m_controls->m_accel = -4.0f; + m_controls->setAccel(-4.0f); m_time_since_reversing += dt; @@ -423,11 +423,11 @@ void ArenaAI::handleArenaBraking() if (forceBraking() && m_kart->getSpeed() > MIN_SPEED) { // Brake now - m_controls->m_brake = true; + m_controls->setBrake(true); return; } - m_controls->m_brake = false; + m_controls->setBrake(false); if (getCurrentNode() == BattleGraph::UNKNOWN_POLY || m_target_node == BattleGraph::UNKNOWN_POLY) return; @@ -437,10 +437,10 @@ void ArenaAI::handleArenaBraking() const float max_turn_speed = m_kart->getSpeedForTurnRadius(m_turn_radius); if (m_kart->getSpeed() > 1.25f * max_turn_speed && - fabsf(m_controls->m_steer) > 0.95f && + fabsf(m_controls->getSteer()) > 0.95f && m_kart->getSpeed() > MIN_SPEED) { - m_controls->m_brake = true; + m_controls->setBrake(true); } } // handleArenaBraking @@ -502,7 +502,7 @@ float ArenaAI::findAngleFrom3Edges(float a, float b, float c) //----------------------------------------------------------------------------- void ArenaAI::handleArenaItems(const float dt) { - m_controls->m_fire = false; + m_controls->setFire(false); if (m_kart->getKartAnimation() || m_kart->getPowerup()->getType() == PowerupManager::POWERUP_NOTHING) return; @@ -544,8 +544,8 @@ void ArenaAI::handleArenaItems(const float dt) ) ) { - m_controls->m_fire = true; - m_controls->m_look_back = false; + m_controls->setFire(true); + m_controls->setLookBack(false); break; } @@ -558,8 +558,8 @@ void ArenaAI::handleArenaItems(const float dt) m_closest_kart_pos_data.distance > 3.0f) || m_time_since_last_shot > 15.0f) { - m_controls->m_fire = true; - m_controls->m_look_back = true; + m_controls->setFire(true); + m_controls->setLookBack(true); break; } @@ -577,8 +577,8 @@ void ArenaAI::handleArenaItems(const float dt) if (m_closest_kart_pos_data.distance < 25.0f && !m_closest_kart->isInvulnerable()) { - m_controls->m_fire = true; - m_controls->m_look_back = fire_behind; + m_controls->setFire(true); + m_controls->setLookBack(fire_behind); break; } @@ -598,8 +598,8 @@ void ArenaAI::handleArenaItems(const float dt) (difficulty || perfect_aim) && !m_closest_kart->isInvulnerable()) { - m_controls->m_fire = true; - m_controls->m_look_back = fire_behind; + m_controls->setFire(true); + m_controls->setLookBack(fire_behind); break; } @@ -618,8 +618,8 @@ void ArenaAI::handleArenaItems(const float dt) m_closest_kart_pos_data.distance < d2 && m_closest_kart->getSpeed() < m_kart->getSpeed()) { - m_controls->m_fire = true; - m_controls->m_look_back = false; + m_controls->setFire(true); + m_controls->setLookBack(false); break; } break; @@ -633,7 +633,7 @@ void ArenaAI::handleArenaItems(const float dt) break; // POWERUP_PLUNGER case PowerupManager::POWERUP_SWITCH: // Don't handle switch - m_controls->m_fire = true; // (use it no matter what) for now + m_controls->setFire(true); // (use it no matter what) for now break; // POWERUP_SWITCH case PowerupManager::POWERUP_PARACHUTE: @@ -651,7 +651,7 @@ void ArenaAI::handleArenaItems(const float dt) m_kart->getPowerup()->getType()); assert(false); } - if (m_controls->m_fire) + if (m_controls->getFire()) m_time_since_last_shot = 0.0f; } // handleArenaItems diff --git a/src/karts/controller/end_controller.cpp b/src/karts/controller/end_controller.cpp index efe1f6134..ba73f68aa 100644 --- a/src/karts/controller/end_controller.cpp +++ b/src/karts/controller/end_controller.cpp @@ -174,10 +174,10 @@ void EndController::action(PlayerAction action, int value) void EndController::update(float dt) { // This is used to enable firing an item backwards. - m_controls->m_look_back = false; - m_controls->m_nitro = false; - m_controls->m_brake = false; - m_controls->m_accel = 1.0f; + m_controls->setLookBack(false); + m_controls->setNitro(false); + m_controls->setBrake(false); + m_controls->setAccel(1.0f); AIBaseLapController::update(dt); @@ -186,10 +186,10 @@ void EndController::update(float dt) race_manager->getMinorMode()==RaceManager::MINOR_MODE_SOCCER || race_manager->getMinorMode()==RaceManager::MINOR_MODE_EASTER_EGG) { - m_controls->m_accel = 0.0f; + m_controls->setAccel(0.0f); // Brake while we are still driving forwards (if we keep // on braking, the kart will reverse otherwise) - m_controls->m_brake = m_kart->getSpeed()>0; + m_controls->setBrake(m_kart->getSpeed()>0); return; } /*Get information that is needed by more than 1 of the handling funcs*/ diff --git a/src/karts/controller/ghost_controller.cpp b/src/karts/controller/ghost_controller.cpp index 4ebf0690a..ce9fa8d96 100644 --- a/src/karts/controller/ghost_controller.cpp +++ b/src/karts/controller/ghost_controller.cpp @@ -54,7 +54,7 @@ void GhostController::update(float dt) if(camera->getKart()!=m_kart) continue; if (camera->getType() != Camera::CM_TYPE_END) { - if (m_controls->m_look_back) + if (m_controls->getLookBack()) { camera->setMode(Camera::CM_REVERSE); } @@ -85,5 +85,5 @@ void GhostController::action(PlayerAction action, int value) { // Watching replay use only if (action == PA_LOOK_BACK) - m_controls->m_look_back = (value!=0); + m_controls->setLookBack(value!=0); } // action diff --git a/src/karts/controller/kart_control.hpp b/src/karts/controller/kart_control.hpp index 95045e62a..ce974f1e4 100644 --- a/src/karts/controller/kart_control.hpp +++ b/src/karts/controller/kart_control.hpp @@ -29,6 +29,12 @@ class KartControl { public: + /** The skidding control state: SC_NONE: not pressed; + * SC_NO_DIRECTION: pressed, but no steering; + * SC_LEFT/RIGHT: pressed in the specified direction. */ + enum SkidControl {SC_NONE, SC_NO_DIRECTION, SC_LEFT, SC_RIGHT}; + +private: /** The current steering value in [-1, 1]. */ float m_steer; /** Acceleration, in [0, 1]. */ @@ -37,17 +43,15 @@ public: bool m_brake; /** True if the kart activates nitro. */ bool m_nitro; - /** The skidding control state: SC_NONE: not pressed; - SC_NO_DIRECTION: pressed, but no steering; - SC_LEFT/RIGHT: pressed in the specified direction. */ - enum SkidControl {SC_NONE, SC_NO_DIRECTION, SC_LEFT, SC_RIGHT} - m_skid; + /** Skidding control state. */ + SkidControl m_skid; /** True if rescue is selected. */ bool m_rescue; /** True if fire is selected. */ bool m_fire; /** True if the kart looks (and shoots) backwards. */ bool m_look_back; +public: KartControl() { @@ -126,6 +130,58 @@ public: m_look_back = (c & 16) != 0; m_skid = (SkidControl)((c & 96) >> 5); } // setButtonsCompressed + // ------------------------------------------------------------------------ + /** Returns the current steering value. */ + float getSteer() const { return m_steer; } + // ------------------------------------------------------------------------ + /** Sets the current steering value. */ + void setSteer(float f) { m_steer = f; } + // ------------------------------------------------------------------------ + /** Returns current acceleration. */ + float getAccel() const { return m_accel; } + // ------------------------------------------------------------------------ + /** Sets the acceleration. */ + void setAccel(float f) { m_accel = f; } + // ------------------------------------------------------------------------ + /** Returns if the kart is braking. */ + bool getBrake() const { return m_brake; } + // ------------------------------------------------------------------------ + /** Sets if the kart is braking. */ + void setBrake(bool b) { m_brake = b; } + // ------------------------------------------------------------------------ + /** Returns if the kart activates nitro. */ + bool getNitro() const { return m_nitro; } + // ------------------------------------------------------------------------ + /** Sets if the kart activates nitro. */ + void setNitro(bool b) { m_nitro = b; } + // ------------------------------------------------------------------------ + /** Returns the skidding control state: SC_NONE: not pressed; + * SC_NO_DIRECTION: pressed, but no steering; + * SC_LEFT/RIGHT: pressed in the specified direction. */ + SkidControl getSkidControl() const { return m_skid; } + // ------------------------------------------------------------------------ + /** Sets the skid control for this kart. */ + void setSkidControl(SkidControl sc) { m_skid = sc; } + // ------------------------------------------------------------------------ + /** Returns true if the kart triggered rescue. */ + bool getRescue() const { return m_rescue; } + // ------------------------------------------------------------------------ + /** Returns if this kart wants to get rescued. */ + void setRescue(bool b) { m_rescue = b; } + // ------------------------------------------------------------------------ + /** Returns if fire is selected. */ + bool getFire() const { return m_fire; } + // ------------------------------------------------------------------------ + /** Sets if the kart wants to fire. */ + void setFire(bool b) { m_fire = b; } + // ------------------------------------------------------------------------ + /** Returns if the kart wants to look back (which also implies that it + * will fire backwards. */ + bool getLookBack() const { return m_look_back; } + // ------------------------------------------------------------------------ + /** Sets if the kart wants to look (and therefore also fires) backwards. */ + void setLookBack(bool b) { m_look_back = b; } + }; #endif diff --git a/src/karts/controller/local_player_controller.cpp b/src/karts/controller/local_player_controller.cpp index 36e8e626f..af6d98299 100644 --- a/src/karts/controller/local_player_controller.cpp +++ b/src/karts/controller/local_player_controller.cpp @@ -147,7 +147,8 @@ void LocalPlayerController::steer(float dt, int steer_val) if(UserConfigParams::m_gamepad_debug) { - Log::debug("LocalPlayerController", " set to: %f\n", m_controls->m_steer); + Log::debug("LocalPlayerController", " set to: %f\n", + m_controls->getSteer()); } } // steer @@ -170,7 +171,7 @@ void LocalPlayerController::update(float dt) Camera *camera = Camera::getCamera(m_camera_index); if (camera->getType() != Camera::CM_TYPE_END) { - if (m_controls->m_look_back || (UserConfigParams::m_reverse_look_threshold > 0 && + if (m_controls->getLookBack() || (UserConfigParams::m_reverse_look_threshold > 0 && m_kart->getSpeed() < -UserConfigParams::m_reverse_look_threshold)) { camera->setMode(Camera::CM_REVERSE); diff --git a/src/karts/controller/player_controller.cpp b/src/karts/controller/player_controller.cpp index 75def44a5..b45dfd748 100644 --- a/src/karts/controller/player_controller.cpp +++ b/src/karts/controller/player_controller.cpp @@ -103,8 +103,8 @@ void PlayerController::action(PlayerAction action, int value) if (value) { m_steer_val = value; - if(m_controls->m_skid==KartControl::SC_NO_DIRECTION) - m_controls->m_skid = KartControl::SC_LEFT; + if(m_controls->getSkidControl()==KartControl::SC_NO_DIRECTION) + m_controls->setSkidControl(KartControl::SC_LEFT); } else m_steer_val = m_steer_val_r; @@ -115,8 +115,8 @@ void PlayerController::action(PlayerAction action, int value) if (value) { m_steer_val = -value; - if(m_controls->m_skid==KartControl::SC_NO_DIRECTION) - m_controls->m_skid = KartControl::SC_RIGHT; + if(m_controls->getSkidControl()==KartControl::SC_NO_DIRECTION) + m_controls->setSkidControl(KartControl::SC_RIGHT); } else m_steer_val = m_steer_val_l; @@ -126,15 +126,15 @@ void PlayerController::action(PlayerAction action, int value) m_prev_accel = value; if (value && !(m_penalty_time > 0.0f)) { - m_controls->m_accel = value/32768.0f; - m_controls->m_brake = false; - m_controls->m_nitro = m_prev_nitro; + m_controls->setAccel(value/32768.0f); + m_controls->setBrake(false); + m_controls->setNitro(m_prev_nitro); } else { - m_controls->m_accel = 0.0f; - m_controls->m_brake = m_prev_brake; - m_controls->m_nitro = false; + m_controls->setAccel(0.0f); + m_controls->setBrake(m_prev_brake); + m_controls->setNitro(false); } break; case PA_BRAKE: @@ -142,44 +142,44 @@ void PlayerController::action(PlayerAction action, int value) // let's consider below that to be a deadzone if(value > 32768/2) { - m_controls->m_brake = true; - m_controls->m_accel = 0.0f; - m_controls->m_nitro = false; + m_controls->setBrake(true); + m_controls->setAccel(0.0f); + m_controls->setNitro(false); } else { - m_controls->m_brake = false; - m_controls->m_accel = m_prev_accel/32768.0f; + m_controls->setBrake(false); + m_controls->setAccel(m_prev_accel/32768.0f); // Nitro still depends on whether we're accelerating - m_controls->m_nitro = (m_prev_nitro && m_prev_accel); + m_controls->setNitro(m_prev_nitro && m_prev_accel); } break; case PA_NITRO: // This basically keeps track whether the button still is being pressed m_prev_nitro = (value != 0); // Enable nitro only when also accelerating - m_controls->m_nitro = ((value!=0) && m_controls->m_accel); + m_controls->setNitro( ((value!=0) && m_controls->getAccel()) ); break; case PA_RESCUE: - m_controls->m_rescue = (value!=0); + m_controls->setRescue(value!=0); break; case PA_FIRE: - m_controls->m_fire = (value!=0); + m_controls->setFire(value!=0); break; case PA_LOOK_BACK: - m_controls->m_look_back = (value!=0); + m_controls->setLookBack(value!=0); break; case PA_DRIFT: if(value==0) - m_controls->m_skid = KartControl::SC_NONE; + m_controls->setSkidControl(KartControl::SC_NONE); else { if(m_steer_val==0) - m_controls->m_skid = KartControl::SC_NO_DIRECTION; + m_controls->setSkidControl(KartControl::SC_NO_DIRECTION); else - m_controls->m_skid = m_steer_val<0 - ? KartControl::SC_RIGHT - : KartControl::SC_LEFT; + m_controls->setSkidControl(m_steer_val<0 + ? KartControl::SC_RIGHT + : KartControl::SC_LEFT ); } break; case PA_PAUSE_RACE: @@ -196,52 +196,54 @@ void PlayerController::action(PlayerAction action, int value) */ void PlayerController::steer(float dt, int steer_val) { + // Get the old value, compute the new steering value, + // and set it at the end of this function + float steer = m_controls->getSteer(); if(stk_config->m_disable_steer_while_unskid && - m_controls->m_skid==KartControl::SC_NONE && + m_controls->getSkidControl()==KartControl::SC_NONE && m_kart->getSkidding()->getVisualSkidRotation()!=0) { - m_controls->m_steer = 0; + steer = 0; } - // Amount the steering is changed for digital devices. // If the steering is 'back to straight', a different steering // change speed is used. - const float STEER_CHANGE = ( (steer_val<=0 && m_controls->m_steer<0) || - (steer_val>=0 && m_controls->m_steer>0) ) + const float STEER_CHANGE = ( (steer_val<=0 && steer<0) || + (steer_val>=0 && steer>0) ) ? dt/m_kart->getKartProperties()->getTurnTimeResetSteer() - : dt/m_kart->getTimeFullSteer(fabsf(m_controls->m_steer)); + : dt/m_kart->getTimeFullSteer(fabsf(steer)); if (steer_val < 0) { // If we got analog values do not cumulate. if (steer_val > -32767) - m_controls->m_steer = -steer_val/32767.0f; + steer = -steer_val/32767.0f; else - m_controls->m_steer += STEER_CHANGE; + steer += STEER_CHANGE; } else if(steer_val > 0) { // If we got analog values do not cumulate. if (steer_val < 32767) - m_controls->m_steer = -steer_val/32767.0f; + steer = -steer_val/32767.0f; else - m_controls->m_steer -= STEER_CHANGE; + steer -= STEER_CHANGE; } else { // no key is pressed - if(m_controls->m_steer>0.0f) + if(steer>0.0f) { - m_controls->m_steer -= STEER_CHANGE; - if(m_controls->m_steer<0.0f) m_controls->m_steer=0.0f; + steer -= STEER_CHANGE; + if(steer<0.0f) steer=0.0f; } else - { // m_controls->m_steer<=0.0f; - m_controls->m_steer += STEER_CHANGE; - if(m_controls->m_steer>0.0f) m_controls->m_steer=0.0f; - } // if m_controls->m_steer<=0.0f + { // steer<=0.0f; + steer += STEER_CHANGE; + if(steer>0.0f) steer=0.0f; + } // if steer<=0.0f } // no key is pressed - m_controls->m_steer = std::min(1.0f, std::max(-1.0f, m_controls->m_steer)); + m_controls->setSteer(std::min(1.0f, std::max(-1.0f, steer)) ); } // steer @@ -251,7 +253,7 @@ void PlayerController::steer(float dt, int steer_val) */ void PlayerController::skidBonusTriggered() { - m_controls->m_steer = 0; + m_controls->setSteer(0); } // skidBonusTriggered //----------------------------------------------------------------------------- @@ -267,15 +269,15 @@ void PlayerController::update(float dt) if (World::getWorld()->getPhase() == World::GOAL_PHASE) { - m_controls->m_brake = false; - m_controls->m_accel = 0.0f; + m_controls->setBrake(false); + m_controls->setAccel(0.0f); return; } if (World::getWorld()->isStartPhase()) { - if (m_controls->m_accel || m_controls->m_brake || - m_controls->m_fire || m_controls->m_nitro) + if (m_controls->getAccel() || m_controls->getBrake()|| + m_controls->getFire() || m_controls->getNitro()) { // Only give penalty time in SET_PHASE. // Penalty time check makes sure it doesn't get rendered on every @@ -287,8 +289,8 @@ void PlayerController::update(float dt) m_penalty_time = stk_config->m_penalty_time; } // if penalty_time = 0 - m_controls->m_brake = false; - m_controls->m_accel = 0.0f; + m_controls->setBrake(false); + m_controls->setAccel(0.0f); } // if key pressed return; @@ -303,10 +305,10 @@ void PlayerController::update(float dt) // Only accept rescue if there is no kart animation is already playing // (e.g. if an explosion happens, wait till the explosion is over before // starting any other animation). - if ( m_controls->m_rescue && !m_kart->getKartAnimation() ) + if ( m_controls->getRescue() && !m_kart->getKartAnimation() ) { new RescueAnimation(m_kart); - m_controls->m_rescue=false; + m_controls->setRescue(false); } } // update diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index 4eb76ba52..d16c9e21a 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -222,8 +222,8 @@ unsigned int SkiddingAI::getNextSector(unsigned int index) void SkiddingAI::update(float dt) { // This is used to enable firing an item backwards. - m_controls->m_look_back = false; - m_controls->m_nitro = false; + m_controls->setLookBack(false); + m_controls->setNitro(false); // Don't do anything if there is currently a kart animations shown. if(m_kart->getKartAnimation()) @@ -323,8 +323,8 @@ void SkiddingAI::update(float dt) m_kart_ahead ) { // Use nitro if the kart is far ahead, or faster than this kart - m_controls->m_nitro = m_distance_ahead>10.0f || - m_kart_ahead->getSpeed() > m_kart->getSpeed(); + m_controls->setNitro(m_distance_ahead>10.0f || + m_kart_ahead->getSpeed() > m_kart->getSpeed()); // If we are close enough, try to hit this kart if(m_distance_ahead<=10) { @@ -354,12 +354,12 @@ void SkiddingAI::update(float dt) handleRescue(dt); handleBraking(); // If a bomb is attached, nitro might already be set. - if(!m_controls->m_nitro) + if(!m_controls->getNitro()) handleNitroAndZipper(); } // If we are supposed to use nitro, but have a zipper, // use the zipper instead (unless there are items to avoid cloe by) - if(m_controls->m_nitro && + if(m_controls->getNitro() && m_kart->getPowerup()->getType()==PowerupManager::POWERUP_ZIPPER && m_kart->getSpeed()>1.0f && m_kart->getSpeedIncreaseTimeLeft(MaxSpeed::MS_INCREASE_ZIPPER)<=0 && @@ -371,8 +371,8 @@ void SkiddingAI::update(float dt) if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_TIME_TRIAL || (m_world->getTime()<3.0f && rand()%50==1) ) { - m_controls->m_nitro = false; - m_controls->m_fire = true; + m_controls->setNitro(false); + m_controls->setFire(true); } } @@ -390,7 +390,7 @@ void SkiddingAI::update(float dt) */ void SkiddingAI::handleBraking() { - m_controls->m_brake = false; + m_controls->setBrake(false); // In follow the leader mode, the kart should brake if they are ahead of // the leader (and not the leader, i.e. don't have initial position 1) if(race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER && @@ -403,7 +403,7 @@ void SkiddingAI::handleBraking() m_kart->getIdent().c_str()); #endif - m_controls->m_brake = true; + m_controls->setBrake(true); return; } @@ -423,7 +423,7 @@ void SkiddingAI::handleBraking() "%s not aligned with track.", m_kart->getIdent().c_str()); #endif - m_controls->m_brake = true; + m_controls->setBrake(true); return; } if(m_current_track_direction==GraphNode::DIR_LEFT || @@ -434,9 +434,9 @@ void SkiddingAI::handleBraking() if(m_kart->getSpeed() > 1.5f*max_turn_speed && m_kart->getSpeed()>MIN_SPEED && - fabsf(m_controls->m_steer) > 0.95f ) + fabsf(m_controls->getSteer()) > 0.95f ) { - m_controls->m_brake = true; + m_controls->setBrake(true); #ifdef DEBUG if(m_ai_debug) Log::debug(getControllerName().c_str(), @@ -1141,7 +1141,7 @@ void SkiddingAI::evaluateItems(const Item *item, float kart_aim_angle, */ void SkiddingAI::handleItems(const float dt) { - m_controls->m_fire = false; + m_controls->setFire(false); if(m_kart->getKartAnimation() || m_kart->getPowerup()->getType() == PowerupManager::POWERUP_NOTHING ) return; @@ -1150,12 +1150,12 @@ void SkiddingAI::handleItems(const float dt) if (m_superpower == RaceManager::SUPERPOWER_NOLOK_BOSS) { - m_controls->m_look_back = (m_kart->getPowerup()->getType() == - PowerupManager::POWERUP_BOWLING ); + m_controls->setLookBack(m_kart->getPowerup()->getType() == + PowerupManager::POWERUP_BOWLING ); if( m_time_since_last_shot > 3.0f ) { - m_controls->m_fire = true; + m_controls->setFire(true); if (m_kart->getPowerup()->getType() == PowerupManager::POWERUP_SWATTER) m_time_since_last_shot = 3.0f; else @@ -1166,7 +1166,7 @@ void SkiddingAI::handleItems(const float dt) } else { - m_controls->m_fire = false; + m_controls->setFire(false); } return; } @@ -1177,7 +1177,7 @@ void SkiddingAI::handleItems(const float dt) { if( m_time_since_last_shot > 10.0f ) { - m_controls->m_fire = true; + m_controls->setFire(true); m_time_since_last_shot = 0.0f; } return; @@ -1202,8 +1202,8 @@ void SkiddingAI::handleItems(const float dt) projectile_manager->projectileIsClose(m_kart, m_ai_properties->m_shield_incoming_radius) ) { - m_controls->m_fire = true; - m_controls->m_look_back = false; + m_controls->setFire(true); + m_controls->setLookBack(false); break; } @@ -1218,8 +1218,8 @@ void SkiddingAI::handleItems(const float dt) // overtaken kart to overtake us again. if(m_distance_behind < 15.0f && m_distance_behind > 3.0f ) { - m_controls->m_fire = true; - m_controls->m_look_back = true; + m_controls->setFire(true); + m_controls->setLookBack(true); break; } @@ -1231,8 +1231,8 @@ void SkiddingAI::handleItems(const float dt) lin_world->getKartLaps(m_kart->getWorldKartId()) == race_manager->getNumLaps()-1) { - m_controls->m_fire = true; - m_controls->m_look_back = true; + m_controls->setFire(true); + m_controls->setLookBack(true); break; } break; // POWERUP_BUBBLEGUM @@ -1278,10 +1278,10 @@ void SkiddingAI::handleItems(const float dt) : m_distance_ahead; // Since cakes can be fired all around, just use a sane distance // with a bit of extra for backwards, as enemy will go towards cake - m_controls->m_fire = (fire_backwards && distance < 25.0f) || - (!fire_backwards && distance < 20.0f); - if(m_controls->m_fire) - m_controls->m_look_back = fire_backwards; + m_controls->setFire( (fire_backwards && distance < 25.0f) || + (!fire_backwards && distance < 20.0f) ); + if(m_controls->getFire()) + m_controls->setLookBack(fire_backwards); break; } // POWERUP_CAKE @@ -1324,12 +1324,12 @@ void SkiddingAI::handleItems(const float dt) float distance = fire_backwards ? m_distance_behind : m_distance_ahead; - m_controls->m_fire = ( (fire_backwards && distance < 30.0f) || + m_controls->setFire( ( (fire_backwards && distance < 30.0f) || (!fire_backwards && distance <10.0f) ) && m_time_since_last_shot > 3.0f && - (straight_behind || straight_ahead); - if(m_controls->m_fire) - m_controls->m_look_back = fire_backwards; + (straight_behind || straight_ahead) ); + if(m_controls->getFire()) + m_controls->setLookBack(fire_backwards); break; } // POWERUP_BOWLING @@ -1355,10 +1355,10 @@ void SkiddingAI::handleItems(const float dt) !m_kart_ahead; float distance = fire_backwards ? m_distance_behind : m_distance_ahead; - m_controls->m_fire = distance < 30.0f || - m_time_since_last_shot > 10.0f; - if(m_controls->m_fire) - m_controls->m_look_back = fire_backwards; + m_controls->setFire(distance < 30.0f || + m_time_since_last_shot > 10.0f ); + if(m_controls->getFire()) + m_controls->setLookBack(fire_backwards); break; } // POWERUP_PLUNGER @@ -1368,13 +1368,13 @@ void SkiddingAI::handleItems(const float dt) // after a waiting an appropriate time if(m_kart->getPosition()>1 && m_time_since_last_shot > stk_config->m_item_switch_time+2.0f) - m_controls->m_fire = true; + m_controls->setFire(true); break; // POWERUP_SWITCH case PowerupManager::POWERUP_PARACHUTE: // Wait one second more than a previous parachute if(m_time_since_last_shot > m_kart->getKartProperties()->getParachuteDurationOther() + 1.0f) - m_controls->m_fire = true; + m_controls->setFire(true); break; // POWERUP_PARACHUTE case PowerupManager::POWERUP_ANVIL: @@ -1383,13 +1383,13 @@ void SkiddingAI::handleItems(const float dt) if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_FOLLOW_LEADER) { - m_controls->m_fire = m_world->getTime()<1.0f && - m_kart->getPosition()>2; + m_controls->setFire(m_world->getTime()<1.0f && + m_kart->getPosition()>2 ); } else { - m_controls->m_fire = m_time_since_last_shot > 3.0f && - m_kart->getPosition()>1; + m_controls->setFire(m_time_since_last_shot > 3.0f && + m_kart->getPosition()>1 ); } break; // POWERUP_ANVIL @@ -1410,7 +1410,7 @@ void SkiddingAI::handleItems(const float dt) m_kart_ahead->getSpeed() < m_kart->getSpeed() ) || ( m_kart_behind && !m_kart_behind->isSquashed() && (m_kart_behind->getXYZ()-m_kart->getXYZ()).length2()m_fire = true; + m_controls->setFire(true); break; } case PowerupManager::POWERUP_RUBBERBALL: @@ -1420,7 +1420,7 @@ void SkiddingAI::handleItems(const float dt) // Perhaps some more sophisticated algorithm might be useful. // For now: fire if there is a kart ahead (which means that // this kart is certainly not the first kart) - m_controls->m_fire = m_kart_ahead != NULL; + m_controls->setFire(m_kart_ahead != NULL); break; default: Log::error(getControllerName().c_str(), @@ -1428,7 +1428,7 @@ void SkiddingAI::handleItems(const float dt) m_kart->getPowerup()->getType()); assert(false); } - if(m_controls->m_fire) m_time_since_last_shot = 0.0f; + if(m_controls->getFire()) m_time_since_last_shot = 0.0f; } // handleItems //----------------------------------------------------------------------------- @@ -1504,26 +1504,26 @@ void SkiddingAI::handleAcceleration( const float dt) if( m_start_delay > 0.0f ) { m_start_delay -= dt; - m_controls->m_accel = 0.0f; + m_controls->setAccel(0.0f); return; } - if( m_controls->m_brake ) + if( m_controls->getBrake()) { - m_controls->m_accel = 0.0f; + m_controls->setAccel(0.0f); return; } if(m_kart->getBlockedByPlungerTime()>0) { if(m_kart->getSpeed() < m_kart->getCurrentMaxSpeed() / 2) - m_controls->m_accel = 0.05f; + m_controls->setAccel(0.05f); else - m_controls->m_accel = 0.0f; + m_controls->setAccel(0.0f); return; } - m_controls->m_accel = stk_config->m_ai_acceleration; + m_controls->setAccel(stk_config->m_ai_acceleration); } // handleAcceleration @@ -1580,7 +1580,7 @@ void SkiddingAI::handleRescue(const float dt) */ void SkiddingAI::handleNitroAndZipper() { - m_controls->m_nitro = false; + m_controls->setNitro(false); // If we are already very fast, save nitro. if(m_kart->getSpeed() > 0.95f*m_kart->getCurrentMaxSpeed()) return; @@ -1588,7 +1588,7 @@ void SkiddingAI::handleNitroAndZipper() if(m_kart->getBlockedByPlungerTime()>0) return; // Don't use nitro if we are braking - if(m_controls->m_brake) return; + if(m_controls->getBrake()) return; // Don't use nitro if the kart is not on ground or has finished the race if(!m_kart->isOnGround() || m_kart->hasFinishedRace()) return; @@ -1618,7 +1618,7 @@ void SkiddingAI::handleNitroAndZipper() // If the kart is very slow (e.g. after rescue), use nitro if(m_kart->getSpeed()<5) { - m_controls->m_nitro = true; + m_controls->setNitro(true); return; } @@ -1629,7 +1629,7 @@ void SkiddingAI::handleNitroAndZipper() if(m_kart->getPosition()== (int)num_karts && num_karts>1 && m_kart->getEnergy()>2.0f) { - m_controls->m_nitro = true; + m_controls->setNitro(true); return; } @@ -1645,7 +1645,7 @@ void SkiddingAI::handleNitroAndZipper() m_world->getEstimatedFinishTime(m_kart->getWorldKartId()); if( 1.5f*m_kart->getEnergy() >= finish - m_world->getTime() ) { - m_controls->m_nitro = true; + m_controls->setNitro(true); return; } } @@ -1661,7 +1661,7 @@ void SkiddingAI::handleNitroAndZipper() m_distance_ahead < overtake_distance && m_kart_ahead->getSpeed()+5.0f > m_kart->getSpeed() ) { - m_controls->m_nitro = true; + m_controls->setNitro(true); return; } @@ -1670,8 +1670,8 @@ void SkiddingAI::handleNitroAndZipper() m_kart_behind->getSpeed() > m_kart->getSpeed() ) { // Only prevent overtaking on highest level - m_controls->m_nitro = m_ai_properties->m_nitro_usage - == AIProperties::NITRO_ALL; + m_controls->setNitro(m_ai_properties->m_nitro_usage + == AIProperties::NITRO_ALL); return; } @@ -1689,7 +1689,7 @@ void SkiddingAI::handleNitroAndZipper() - QuadGraph::get()->getDistanceFromStart(m_track_node); if(diff<0) diff+=World::getWorld()->getTrack()->getTrackLength(); if(diff>m_ai_properties->m_straight_length_for_zipper) - m_controls->m_fire = true; + m_controls->setFire(true); } } @@ -2222,7 +2222,7 @@ bool SkiddingAI::canSkid(float steer_fraction) // If the kart has to do a sharp turn, but is already skidding, find // a good time to release the skid button, since this will turn the // kart more sharply: - if(m_controls->m_skid) + if(m_controls->getSkidControl()) { #ifdef DEBUG if(m_ai_debug) @@ -2247,7 +2247,7 @@ bool SkiddingAI::canSkid(float steer_fraction) m_current_track_direction==GraphNode::DIR_UNDEFINED ) { #ifdef DEBUG - if(m_controls->m_skid && m_ai_debug) + if(m_controls->getSkidControl() && m_ai_debug) { Log::debug(getControllerName().c_str(), "%s stops skidding on straight.", @@ -2283,7 +2283,7 @@ bool SkiddingAI::canSkid(float steer_fraction) // If the remaining estimated time for skidding is too short, stop // it. This code will mostly trigger the bonus at the end of a skid. - if(m_controls->m_skid && duration < 1.0f) + if(m_controls->getSkidControl() && duration < 1.0f) { if(m_ai_debug) Log::debug(getControllerName().c_str(), @@ -2301,7 +2301,7 @@ bool SkiddingAI::canSkid(float steer_fraction) m_current_track_direction==GraphNode::DIR_RIGHT) ) { #ifdef DEBUG - if(m_controls->m_skid && m_ai_debug) + if(m_controls->getSkidControl() && m_ai_debug) Log::debug(getControllerName().c_str(), "%s skidding against track direction.", m_kart->getIdent().c_str()); @@ -2313,7 +2313,7 @@ bool SkiddingAI::canSkid(float steer_fraction) m_kart->getKartProperties()->getSkidTimeTillBonus()[0] < duration) { #ifdef DEBUG - if(!m_controls->m_skid && m_ai_debug) + if(!m_controls->getSkidControl() && m_ai_debug) Log::debug(getControllerName().c_str(), "%s start skid, duration %f.", m_kart->getIdent().c_str(), duration); @@ -2323,7 +2323,7 @@ bool SkiddingAI::canSkid(float steer_fraction) } // if curve long enough for skidding #ifdef DEBUG - if(m_controls->m_skid && m_ai_debug) + if(m_controls->getSkidControl() && m_ai_debug) Log::debug(getControllerName().c_str(), "%s has no reasons to skid anymore.", m_kart->getIdent().c_str()); @@ -2354,7 +2354,7 @@ void SkiddingAI::setSteering(float angle, float dt) if(!canSkid(steer_fraction)) { m_skid_probability_state = SKID_PROBAB_NOT_YET; - m_controls->m_skid = KartControl::SC_NONE; + m_controls->setSkidControl(KartControl::SC_NONE); } else { @@ -2377,8 +2377,8 @@ void SkiddingAI::setSteering(float angle, float dt) sc= ? "no" : sc==KartControl::SC_LEFT ? "left" : "right"); #endif } - m_controls->m_skid = m_skid_probability_state == SKID_PROBAB_SKID - ? sc : KartControl::SC_NONE; + m_controls->setSkidControl(m_skid_probability_state == SKID_PROBAB_SKID + ? sc : KartControl::SC_NONE ); } // Adjust steer fraction in case to be in [-1,1] @@ -2402,7 +2402,7 @@ void SkiddingAI::setSteering(float angle, float dt) if((ss==Skidding::SKID_ACCUMULATE_LEFT && steer_fraction>0.2f ) || (ss==Skidding::SKID_ACCUMULATE_RIGHT && steer_fraction<-0.2f) ) { - m_controls->m_skid = KartControl::SC_NONE; + m_controls->setSkidControl(KartControl::SC_NONE); #ifdef DEBUG if(m_ai_debug) Log::info(getControllerName().c_str(), @@ -2411,7 +2411,7 @@ void SkiddingAI::setSteering(float angle, float dt) #endif } - if(m_controls->m_skid!=KartControl::SC_NONE && + if(m_controls->getSkidControl()!=KartControl::SC_NONE && ( ss==Skidding::SKID_ACCUMULATE_LEFT || ss==Skidding::SKID_ACCUMULATE_RIGHT ) ) { @@ -2425,7 +2425,7 @@ void SkiddingAI::setSteering(float angle, float dt) "%s steering too much (%f).", m_kart->getIdent().c_str(), steer_fraction); #endif - m_controls->m_skid = KartControl::SC_NONE; + m_controls->setSkidControl(KartControl::SC_NONE); } if(steer_fraction<-1.0f) steer_fraction = -1.0f; @@ -2433,19 +2433,19 @@ void SkiddingAI::setSteering(float angle, float dt) steer_fraction = 1.0f; } - float old_steer = m_controls->m_steer; + float old_steer = m_controls->getSteer(); // The AI has its own 'time full steer' value (which is the time float max_steer_change = dt/m_ai_properties->m_time_full_steer; if(old_steer < steer_fraction) { - m_controls->m_steer = (old_steer+max_steer_change > steer_fraction) - ? steer_fraction : old_steer+max_steer_change; + m_controls->setSteer( (old_steer+max_steer_change > steer_fraction) + ? steer_fraction : old_steer+max_steer_change ); } else { - m_controls->m_steer = (old_steer-max_steer_change < steer_fraction) - ? steer_fraction : old_steer-max_steer_change; + m_controls->setSteer( (old_steer-max_steer_change < steer_fraction) + ? steer_fraction : old_steer-max_steer_change ); } diff --git a/src/karts/controller/soccer_ai.cpp b/src/karts/controller/soccer_ai.cpp index 85ff79222..73b9e3d83 100644 --- a/src/karts/controller/soccer_ai.cpp +++ b/src/karts/controller/soccer_ai.cpp @@ -118,8 +118,8 @@ void SoccerAI::update(float dt) if (m_world->getPhase() == World::GOAL_PHASE) { resetAfterStop(); - m_controls->m_brake = false; - m_controls->m_accel = 0.0f; + m_controls->setBrake(false); + m_controls->setAccel(0.0f); AIBaseController::update(dt); return; } diff --git a/src/karts/controller/test_ai.cpp b/src/karts/controller/test_ai.cpp index 8e13e8e5a..49ef406ef 100644 --- a/src/karts/controller/test_ai.cpp +++ b/src/karts/controller/test_ai.cpp @@ -228,8 +228,8 @@ unsigned int SkiddingAI::getNextSector(unsigned int index) void SkiddingAI::update(float dt) { // This is used to enable firing an item backwards. - m_controls->m_look_back = false; - m_controls->m_nitro = false; + m_controls->setLookBack(false); + m_controls->setNitro(false); // Don't do anything if there is currently a kart animations shown. if(m_kart->getKartAnimation()) @@ -329,8 +329,8 @@ void SkiddingAI::update(float dt) m_kart_ahead ) { // Use nitro if the kart is far ahead, or faster than this kart - m_controls->m_nitro = m_distance_ahead>10.0f || - m_kart_ahead->getSpeed() > m_kart->getSpeed(); + m_controls->setNitro(m_distance_ahead>10.0f || + m_kart_ahead->getSpeed() > m_kart->getSpeed()); // If we are close enough, try to hit this kart if(m_distance_ahead<=10) { @@ -360,12 +360,12 @@ void SkiddingAI::update(float dt) handleRescue(dt); handleBraking(); // If a bomb is attached, nitro might already be set. - if(!m_controls->m_nitro) + if(!m_controls->getNitro()) handleNitroAndZipper(); } // If we are supposed to use nitro, but have a zipper, // use the zipper instead (unless there are items to avoid cloe by) - if(m_controls->m_nitro && + if(m_controls->getNitro() && m_kart->getPowerup()->getType()==PowerupManager::POWERUP_ZIPPER && m_kart->getSpeed()>1.0f && m_kart->getSpeedIncreaseTimeLeft(MaxSpeed::MS_INCREASE_ZIPPER)<=0 && @@ -377,8 +377,8 @@ void SkiddingAI::update(float dt) if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_TIME_TRIAL || (m_world->getTime()<3.0f && rand()%50==1) ) { - m_controls->m_nitro = false; - m_controls->m_fire = true; + m_controls->setNitro(false); + m_controls->setFire(true); } } @@ -396,7 +396,7 @@ void SkiddingAI::update(float dt) */ void SkiddingAI::handleBraking() { - m_controls->m_brake = false; + m_controls->setBrake(false); // In follow the leader mode, the kart should brake if they are ahead of // the leader (and not the leader, i.e. don't have initial position 1) if(race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER && @@ -409,7 +409,7 @@ void SkiddingAI::handleBraking() m_kart->getIdent().c_str()); #endif - m_controls->m_brake = true; + m_controls->setBrake(true); return; } @@ -429,7 +429,7 @@ void SkiddingAI::handleBraking() "%s not aligned with track.", m_kart->getIdent().c_str()); #endif - m_controls->m_brake = true; + m_controls->setBrake(true); return; } if(m_current_track_direction==GraphNode::DIR_LEFT || @@ -440,9 +440,9 @@ void SkiddingAI::handleBraking() if(m_kart->getSpeed() > 1.5f*max_turn_speed && m_kart->getSpeed()>MIN_SPEED && - fabsf(m_controls->m_steer) > 0.95f ) + fabsf(m_controls->getSteer()) > 0.95f ) { - m_controls->m_brake = true; + m_controls->setBrake(true); #ifdef DEBUG if(m_ai_debug) Log::debug(getControllerName().c_str(), @@ -1147,7 +1147,7 @@ void SkiddingAI::evaluateItems(const Item *item, float kart_aim_angle, */ void SkiddingAI::handleItems(const float dt) { - m_controls->m_fire = false; + m_controls->setFire(false); if(m_kart->getKartAnimation() || m_kart->getPowerup()->getType() == PowerupManager::POWERUP_NOTHING ) return; @@ -1156,12 +1156,12 @@ void SkiddingAI::handleItems(const float dt) if (m_superpower == RaceManager::SUPERPOWER_NOLOK_BOSS) { - m_controls->m_look_back = (m_kart->getPowerup()->getType() == - PowerupManager::POWERUP_BOWLING ); + m_controls->setLookBack(m_kart->getPowerup()->getType() == + PowerupManager::POWERUP_BOWLING ); if( m_time_since_last_shot > 3.0f ) { - m_controls->m_fire = true; + m_controls->setFire(true); if (m_kart->getPowerup()->getType() == PowerupManager::POWERUP_SWATTER) m_time_since_last_shot = 3.0f; else @@ -1172,7 +1172,7 @@ void SkiddingAI::handleItems(const float dt) } else { - m_controls->m_fire = false; + m_controls->setFire(false); } return; } @@ -1183,7 +1183,7 @@ void SkiddingAI::handleItems(const float dt) { if( m_time_since_last_shot > 10.0f ) { - m_controls->m_fire = true; + m_controls->setFire(true); m_time_since_last_shot = 0.0f; } return; @@ -1208,8 +1208,8 @@ void SkiddingAI::handleItems(const float dt) projectile_manager->projectileIsClose(m_kart, m_ai_properties->m_shield_incoming_radius) ) { - m_controls->m_fire = true; - m_controls->m_look_back = false; + m_controls->setFire(true); + m_controls->setLookBack(false); break; } @@ -1224,8 +1224,8 @@ void SkiddingAI::handleItems(const float dt) // overtaken kart to overtake us again. if(m_distance_behind < 15.0f && m_distance_behind > 3.0f ) { - m_controls->m_fire = true; - m_controls->m_look_back = true; + m_controls->setFire(true); + m_controls->setLookBack(true); break; } @@ -1237,8 +1237,8 @@ void SkiddingAI::handleItems(const float dt) lin_world->getKartLaps(m_kart->getWorldKartId()) == race_manager->getNumLaps()-1) { - m_controls->m_fire = true; - m_controls->m_look_back = true; + m_controls->setFire(true); + m_controls->setLookBack(true); break; } break; // POWERUP_BUBBLEGUM @@ -1284,10 +1284,10 @@ void SkiddingAI::handleItems(const float dt) : m_distance_ahead; // Since cakes can be fired all around, just use a sane distance // with a bit of extra for backwards, as enemy will go towards cake - m_controls->m_fire = (fire_backwards && distance < 25.0f) || - (!fire_backwards && distance < 20.0f); - if(m_controls->m_fire) - m_controls->m_look_back = fire_backwards; + m_controls->setFire( (fire_backwards && distance < 25.0f) || + (!fire_backwards && distance < 20.0f) ); + if(m_controls->getFire()) + m_controls->setLookBack(fire_backwards); break; } // POWERUP_CAKE @@ -1330,12 +1330,12 @@ void SkiddingAI::handleItems(const float dt) float distance = fire_backwards ? m_distance_behind : m_distance_ahead; - m_controls->m_fire = ( (fire_backwards && distance < 30.0f) || + m_controls->setFire( ( (fire_backwards && distance < 30.0f) || (!fire_backwards && distance <10.0f) ) && m_time_since_last_shot > 3.0f && - (straight_behind || straight_ahead); - if(m_controls->m_fire) - m_controls->m_look_back = fire_backwards; + (straight_behind || straight_ahead) ); + if(m_controls->getFire()) + m_controls->setLookBack(fire_backwards); break; } // POWERUP_BOWLING @@ -1361,10 +1361,10 @@ void SkiddingAI::handleItems(const float dt) !m_kart_ahead; float distance = fire_backwards ? m_distance_behind : m_distance_ahead; - m_controls->m_fire = distance < 30.0f || - m_time_since_last_shot > 10.0f; - if(m_controls->m_fire) - m_controls->m_look_back = fire_backwards; + m_controls->setFire(distance < 30.0f || + m_time_since_last_shot > 10.0f ); + if(m_controls->getFire()) + m_controls->setLookBack(fire_backwards); break; } // POWERUP_PLUNGER @@ -1374,13 +1374,13 @@ void SkiddingAI::handleItems(const float dt) // after a waiting an appropriate time if(m_kart->getPosition()>1 && m_time_since_last_shot > stk_config->m_item_switch_time+2.0f) - m_controls->m_fire = true; + m_controls->setFire(true); break; // POWERUP_SWITCH case PowerupManager::POWERUP_PARACHUTE: // Wait one second more than a previous parachute if(m_time_since_last_shot > m_kart->getKartProperties()->getParachuteDurationOther() + 1.0f) - m_controls->m_fire = true; + m_controls->setFire(true); break; // POWERUP_PARACHUTE case PowerupManager::POWERUP_ANVIL: @@ -1389,13 +1389,13 @@ void SkiddingAI::handleItems(const float dt) if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_FOLLOW_LEADER) { - m_controls->m_fire = m_world->getTime()<1.0f && - m_kart->getPosition()>2; + m_controls->setFire(m_world->getTime()<1.0f && + m_kart->getPosition()>2 ); } else { - m_controls->m_fire = m_time_since_last_shot > 3.0f && - m_kart->getPosition()>1; + m_controls->setFire(m_time_since_last_shot > 3.0f && + m_kart->getPosition()>1 ); } break; // POWERUP_ANVIL @@ -1416,7 +1416,7 @@ void SkiddingAI::handleItems(const float dt) m_kart_ahead->getSpeed() < m_kart->getSpeed() ) || ( m_kart_behind && !m_kart_behind->isSquashed() && (m_kart_behind->getXYZ()-m_kart->getXYZ()).length2()m_fire = true; + m_controls->setFire(true); break; } case PowerupManager::POWERUP_RUBBERBALL: @@ -1426,7 +1426,7 @@ void SkiddingAI::handleItems(const float dt) // Perhaps some more sophisticated algorithm might be useful. // For now: fire if there is a kart ahead (which means that // this kart is certainly not the first kart) - m_controls->m_fire = m_kart_ahead != NULL; + m_controls->setFire(m_kart_ahead != NULL); break; default: Log::error(getControllerName().c_str(), @@ -1434,7 +1434,7 @@ void SkiddingAI::handleItems(const float dt) m_kart->getPowerup()->getType()); assert(false); } - if(m_controls->m_fire) m_time_since_last_shot = 0.0f; + if(m_controls->getFire()) m_time_since_last_shot = 0.0f; } // handleItems //----------------------------------------------------------------------------- @@ -1510,7 +1510,7 @@ void SkiddingAI::handleAcceleration( const float dt) if( m_start_delay > 0.0f ) { m_start_delay -= dt; - m_controls->m_accel = 0.0f; + m_controls->setAccel(0.0f); return; } @@ -1518,22 +1518,22 @@ void SkiddingAI::handleAcceleration( const float dt) // m_brake has not been reset from the previous frame, which can // cause too long slow downs. On the other hand removing it appears // to decrease performance in some narrower tracks - if( m_controls->m_brake ) + if( m_controls->getBrake()) { - m_controls->m_accel = 0.0f; + m_controls->setAccel(0.0f); return; } if(m_kart->getBlockedByPlungerTime()>0) { if(m_kart->getSpeed() < m_kart->getCurrentMaxSpeed() / 2) - m_controls->m_accel = 0.05f; + m_controls->setAccel(0.05f); else - m_controls->m_accel = 0.0f; + m_controls->setAccel(0.0f); return; } - m_controls->m_accel = stk_config->m_ai_acceleration; + m_controls->setAccel(stk_config->m_ai_acceleration); } // handleAcceleration @@ -1590,7 +1590,7 @@ void SkiddingAI::handleRescue(const float dt) */ void SkiddingAI::handleNitroAndZipper() { - m_controls->m_nitro = false; + m_controls->setNitro(false); // The next line prevents usage of nitro on long straights, where the kart // is already fast. @@ -1599,14 +1599,14 @@ void SkiddingAI::handleNitroAndZipper() if(m_kart->getSpeed() > 0.95f*m_kart->getCurrentMaxSpeed()) return; // About the above: removing this line might enable the AI to better use - // nitro and zippers . + // nitro and zippers. // This works well for some tracks (math, lighthouse // sandtrack), but worse in others (zen, xr591). The good result // is caused by long straights in which the AI will now use zippers, // but in the other tracks the high speed causes karts to crash in // tight corners. In some cases it would need a reasonable large 'lookahead' // to know that at a certain spot a zipper or skidding should not be used - // (e.g. in xr591, left at the fork - new AI will nearly always fell of the + // (e.g. in xr591, left at the fork - new AI will nearly always fall off the // track after the curve). Here the summarised results of 10 laps runs with // 4 old against 4 new AIs in time trail mode. The 'gain' is the sum of all // (star_positions-end_positions). So a gain of -10 means that basically @@ -1637,7 +1637,7 @@ void SkiddingAI::handleNitroAndZipper() if(m_kart->getBlockedByPlungerTime()>0) return; // Don't use nitro if we are braking - if(m_controls->m_brake) return; + if(m_controls->getBrake()) return; // Don't use nitro if the kart is not on ground or has finished the race if(!m_kart->isOnGround() || m_kart->hasFinishedRace()) return; @@ -1667,7 +1667,7 @@ void SkiddingAI::handleNitroAndZipper() // If the kart is very slow (e.g. after rescue), use nitro if(m_kart->getSpeed()<5) { - m_controls->m_nitro = true; + m_controls->setNitro(true); return; } @@ -1678,7 +1678,7 @@ void SkiddingAI::handleNitroAndZipper() if(m_kart->getPosition()== (int)num_karts && num_karts>1 && m_kart->getEnergy()>2.0f) { - m_controls->m_nitro = true; + m_controls->setNitro(true); return; } @@ -1694,7 +1694,7 @@ void SkiddingAI::handleNitroAndZipper() m_world->getEstimatedFinishTime(m_kart->getWorldKartId()); if( 1.5f*m_kart->getEnergy() >= finish - m_world->getTime() ) { - m_controls->m_nitro = true; + m_controls->setNitro(true); return; } } @@ -1710,7 +1710,7 @@ void SkiddingAI::handleNitroAndZipper() m_distance_ahead < overtake_distance && m_kart_ahead->getSpeed()+5.0f > m_kart->getSpeed() ) { - m_controls->m_nitro = true; + m_controls->setNitro(true); return; } @@ -1719,8 +1719,8 @@ void SkiddingAI::handleNitroAndZipper() m_kart_behind->getSpeed() > m_kart->getSpeed() ) { // Only prevent overtaking on highest level - m_controls->m_nitro = m_ai_properties->m_nitro_usage - == AIProperties::NITRO_ALL; + m_controls->setNitro(m_ai_properties->m_nitro_usage + == AIProperties::NITRO_ALL ); return; } @@ -1738,7 +1738,7 @@ void SkiddingAI::handleNitroAndZipper() - QuadGraph::get()->getDistanceFromStart(m_track_node); if(diff<0) diff+=World::getWorld()->getTrack()->getTrackLength(); if(diff>m_ai_properties->m_straight_length_for_zipper) - m_controls->m_fire = true; + m_controls->setFire(true); } } @@ -2271,7 +2271,7 @@ bool SkiddingAI::canSkid(float steer_fraction) // If the kart has to do a sharp turn, but is already skidding, find // a good time to release the skid button, since this will turn the // kart more sharply: - if(m_controls->m_skid) + if(m_controls->getSkidControl()) { #ifdef DEBUG if(m_ai_debug) @@ -2296,7 +2296,7 @@ bool SkiddingAI::canSkid(float steer_fraction) m_current_track_direction==GraphNode::DIR_UNDEFINED ) { #ifdef DEBUG - if(m_controls->m_skid && m_ai_debug) + if(m_controls->getSkidControl() && m_ai_debug) { Log::debug(getControllerName().c_str(), "%s stops skidding on straight.", @@ -2332,7 +2332,7 @@ bool SkiddingAI::canSkid(float steer_fraction) // If the remaining estimated time for skidding is too short, stop // it. This code will mostly trigger the bonus at the end of a skid. - if(m_controls->m_skid && duration < 1.0f) + if(m_controls->getSkidControl() && duration < 1.0f) { if(m_ai_debug) Log::debug(getControllerName().c_str(), @@ -2350,7 +2350,7 @@ bool SkiddingAI::canSkid(float steer_fraction) m_current_track_direction==GraphNode::DIR_RIGHT) ) { #ifdef DEBUG - if(m_controls->m_skid && m_ai_debug) + if(m_controls->getSkidControl() && m_ai_debug) Log::debug(getControllerName().c_str(), "%s skidding against track direction.", m_kart->getIdent().c_str()); @@ -2362,7 +2362,7 @@ bool SkiddingAI::canSkid(float steer_fraction) m_kart->getKartProperties()->getSkidTimeTillBonus()[0] < duration) { #ifdef DEBUG - if(!m_controls->m_skid && m_ai_debug) + if(!m_controls->getSkidControl() && m_ai_debug) Log::debug(getControllerName().c_str(), "%s start skid, duration %f.", m_kart->getIdent().c_str(), duration); @@ -2372,7 +2372,7 @@ bool SkiddingAI::canSkid(float steer_fraction) } // if curve long enough for skidding #ifdef DEBUG - if(m_controls->m_skid && m_ai_debug) + if(m_controls->getSkidControl() && m_ai_debug) Log::debug(getControllerName().c_str(), "%s has no reasons to skid anymore.", m_kart->getIdent().c_str()); @@ -2403,7 +2403,7 @@ void SkiddingAI::setSteering(float angle, float dt) if(!canSkid(steer_fraction)) { m_skid_probability_state = SKID_PROBAB_NOT_YET; - m_controls->m_skid = KartControl::SC_NONE; + m_controls->setSkidControl(KartControl::SC_NONE); } else { @@ -2426,8 +2426,8 @@ void SkiddingAI::setSteering(float angle, float dt) sc= ? "no" : sc==KartControl::SC_LEFT ? "left" : "right"); #endif } - m_controls->m_skid = m_skid_probability_state == SKID_PROBAB_SKID - ? sc : KartControl::SC_NONE; + m_controls->setSkidControl(m_skid_probability_state == SKID_PROBAB_SKID + ? sc : KartControl::SC_NONE ); } // Adjust steer fraction in case to be in [-1,1] @@ -2451,7 +2451,7 @@ void SkiddingAI::setSteering(float angle, float dt) if((ss==Skidding::SKID_ACCUMULATE_LEFT && steer_fraction>0.2f ) || (ss==Skidding::SKID_ACCUMULATE_RIGHT && steer_fraction<-0.2f) ) { - m_controls->m_skid = KartControl::SC_NONE; + m_controls->setSkidControl(KartControl::SC_NONE); #ifdef DEBUG if(m_ai_debug) Log::info(getControllerName().c_str(), @@ -2460,7 +2460,7 @@ void SkiddingAI::setSteering(float angle, float dt) #endif } - if(m_controls->m_skid!=KartControl::SC_NONE && + if(m_controls->getSkidControl()!=KartControl::SC_NONE && ( ss==Skidding::SKID_ACCUMULATE_LEFT || ss==Skidding::SKID_ACCUMULATE_RIGHT ) ) { @@ -2474,7 +2474,7 @@ void SkiddingAI::setSteering(float angle, float dt) "%s steering too much (%f).", m_kart->getIdent().c_str(), steer_fraction); #endif - m_controls->m_skid = KartControl::SC_NONE; + m_controls->setSkidControl(KartControl::SC_NONE); } if(steer_fraction<-1.0f) steer_fraction = -1.0f; @@ -2482,19 +2482,19 @@ void SkiddingAI::setSteering(float angle, float dt) steer_fraction = 1.0f; } - float old_steer = m_controls->m_steer; + float old_steer = m_controls->getSteer(); // The AI has its own 'time full steer' value (which is the time float max_steer_change = dt/m_ai_properties->m_time_full_steer; if(old_steer < steer_fraction) { - m_controls->m_steer = (old_steer+max_steer_change > steer_fraction) - ? steer_fraction : old_steer+max_steer_change; + m_controls->setSteer( (old_steer+max_steer_change > steer_fraction) + ? steer_fraction : old_steer+max_steer_change ); } else { - m_controls->m_steer = (old_steer-max_steer_change < steer_fraction) - ? steer_fraction : old_steer-max_steer_change; + m_controls->setSteer( (old_steer-max_steer_change < steer_fraction) + ? steer_fraction : old_steer-max_steer_change ); } diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index bbdc67d5a..f8ae75598 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1277,9 +1277,9 @@ void Kart::update(float dt) updatePhysics(dt); PROFILER_POP_CPU_MARKER(); - if(!m_controls.m_fire) m_fire_clicked = 0; + if(!m_controls.getFire()) m_fire_clicked = 0; - if(m_controls.m_fire && !m_fire_clicked && !m_kart_animation) + if(m_controls.getFire() && !m_fire_clicked && !m_kart_animation) { // use() needs to be called even if there currently is no collecteable // since use() can test if something needs to be switched on/off. @@ -1791,7 +1791,7 @@ void Kart::handleZipper(const Material *material, bool play_sound) engine_force = m_kart_properties->getZipperForce(); } // Ignore a zipper that's activated while braking - if(m_controls.m_brake || m_speed<0) return; + if(m_controls.getBrake() || m_speed<0) return; m_max_speed->instantSpeedIncrease(MaxSpeed::MS_INCREASE_ZIPPER, max_speed_increase, speed_gain, @@ -1807,7 +1807,7 @@ void Kart::handleZipper(const Material *material, bool play_sound) */ void Kart::updateNitro(float dt) { - if (m_controls.m_nitro && m_min_nitro_time <= 0.0f) + if (m_controls.getNitro() && m_min_nitro_time <= 0.0f) { m_min_nitro_time = m_kart_properties->getNitroMinConsumptionTime(); } @@ -1817,11 +1817,11 @@ void Kart::updateNitro(float dt) // when pressing the key, don't allow the min time to go under zero. // If it went under zero, it would be reset - if (m_controls.m_nitro && m_min_nitro_time <= 0.0f) + if (m_controls.getNitro() && m_min_nitro_time <= 0.0f) m_min_nitro_time = 0.1f; } - bool increase_speed = (m_controls.m_nitro && isOnGround()); + bool increase_speed = (m_controls.getNitro() && isOnGround()); if (!increase_speed && m_min_nitro_time <= 0.0f) { if(m_nitro_sound->getStatus() == SFXBase::SFX_PLAYING) @@ -2132,7 +2132,7 @@ void Kart::updatePhysics(float dt) // Check if accel is pressed for the first time. The actual timing // is done in getStartupBoost - it returns 0 if the start was actually // too slow to qualify for a boost. - if(!m_has_started && m_controls.m_accel) + if(!m_has_started && m_controls.getAccel()) { m_has_started = true; float f = getStartupBoost(); @@ -2155,8 +2155,8 @@ void Kart::updatePhysics(float dt) if (m_flying) updateFlying(); - m_skidding->update(dt, isOnGround(), m_controls.m_steer, - m_controls.m_skid); + m_skidding->update(dt, isOnGround(), m_controls.getSteer(), + m_controls.getSkidControl()); m_vehicle->setVisualRotation(m_skidding->getVisualSkidRotation()); if(( m_skidding->getSkidState() == Skidding::SKID_ACCUMULATE_LEFT || m_skidding->getSkidState() == Skidding::SKID_ACCUMULATE_RIGHT ) && @@ -2296,7 +2296,7 @@ void Kart::updateEnginePowerAndBrakes(float dt) m_body->applyTorque(btVector3(0.0, m_bubblegum_torque, 0.0)); } - if(m_controls.m_accel) // accelerating + if(m_controls.getAccel()) // accelerating { // For a short time after a collision disable the engine, // so that the karts can bounce back a bit from the obstacle. @@ -2309,11 +2309,11 @@ void Kart::updateEnginePowerAndBrakes(float dt) engine_power *= 5.0f; // Lose some traction when skidding, to balance the advantage - if (m_controls.m_skid && + if (m_controls.getSkidControl() && m_kart_properties->getSkidVisualTime() == 0) engine_power *= 0.5f; - applyEngineForce(engine_power*m_controls.m_accel); + applyEngineForce(engine_power*m_controls.getAccel()); // Either all or no brake is set, so test only one to avoid // resetting all brakes most of the time. @@ -2324,7 +2324,7 @@ void Kart::updateEnginePowerAndBrakes(float dt) } else { // not accelerating - if(m_controls.m_brake) + if(m_controls.getBrake()) { // check if the player is currently only slowing down // or moving backwards if(m_speed > 0.0f) @@ -2360,9 +2360,9 @@ void Kart::updateEnginePowerAndBrakes(float dt) { m_brake_time = 0; // lift the foot from throttle, brakes with 10% engine_power - assert(!std::isnan(m_controls.m_accel)); + assert(!std::isnan(m_controls.getAccel())); assert(!std::isnan(engine_power)); - applyEngineForce(-m_controls.m_accel*engine_power*0.1f); + applyEngineForce(-m_controls.getAccel()*engine_power*0.1f); // If not giving power (forward or reverse gear), and speed is low // we are "parking" the kart, so in battle mode we can ambush people @@ -2431,7 +2431,7 @@ void Kart::updateFlying() { m_body->setLinearVelocity(m_body->getLinearVelocity() * 0.99f); - if (m_controls.m_accel) + if (m_controls.getAccel()) { btVector3 velocity = m_body->getLinearVelocity(); if (velocity.length() < 25) @@ -2441,7 +2441,7 @@ void Kart::updateFlying() 100.0f*cos(orientation))); } } - else if (m_controls.m_brake) + else if (m_controls.getBrake()) { btVector3 velocity = m_body->getLinearVelocity(); if (velocity.length() > -15) @@ -2452,9 +2452,9 @@ void Kart::updateFlying() } } - if (m_controls.m_steer != 0.0f) + if (m_controls.getSteer()!= 0.0f) { - m_body->applyTorque(btVector3(0.0, m_controls.m_steer * 3500.0f, 0.0)); + m_body->applyTorque(btVector3(0.0, m_controls.getSteer()*3500.0f, 0.0)); } // dampen any roll while flying, makes the kart hard to control @@ -2690,7 +2690,7 @@ void Kart::updateGraphics(float dt, const Vec3& offset_xyz, // depending on speed) // -------------------------------------------------------- float nitro_frac = 0; - if ( (m_controls.m_nitro || m_min_nitro_time > 0.0f) && + if ( (m_controls.getNitro() || m_min_nitro_time > 0.0f) && isOnGround() && m_collected_energy > 0 ) { // fabs(speed) is important, otherwise the negative number will diff --git a/src/karts/kart_gfx.cpp b/src/karts/kart_gfx.cpp index 5d6a830e0..989495245 100644 --- a/src/karts/kart_gfx.cpp +++ b/src/karts/kart_gfx.cpp @@ -324,7 +324,7 @@ void KartGFX::updateTerrain(const ParticleKind *pk) bool on_ground = m_kart->isOnGround() && m_kart->getSkidding()->getGraphicalJumpOffset()==0; if (skidding > 1.0f && on_ground) - rate = fabsf(m_kart->getControls().m_steer) > 0.8 ? skidding - 1 : 0; + rate = fabsf(m_kart->getControls().getSteer()) > 0.8 ? skidding - 1 : 0; else if (speed >= 0.5f && on_ground) rate = speed/m_kart->getKartProperties()->getEngineMaxSpeed(); else diff --git a/src/karts/kart_with_stats.cpp b/src/karts/kart_with_stats.cpp index 15851c9b7..67ef5abfc 100644 --- a/src/karts/kart_with_stats.cpp +++ b/src/karts/kart_with_stats.cpp @@ -61,11 +61,9 @@ void KartWithStats::reset() void KartWithStats::update(float dt) { Kart::update(dt); - if(getSpeed()>m_top_speed) m_top_speed = getSpeed(); - if(getControls().m_skid) - m_skidding_time += dt; - if(getControls().m_brake) - m_brake_count ++; + if(getSpeed()>m_top_speed ) m_top_speed = getSpeed(); + if(getControls().getSkidControl()) m_skidding_time += dt; + if(getControls().getBrake() ) m_brake_count ++; LinearWorld *world = dynamic_cast(World::getWorld()); if(world && !world->isOnRoad(getWorldKartId())) m_off_track_count ++; diff --git a/src/karts/skidding.cpp b/src/karts/skidding.cpp index 58e4466f2..3ed348428 100644 --- a/src/karts/skidding.cpp +++ b/src/karts/skidding.cpp @@ -77,7 +77,7 @@ void Skidding::reset() m_kart->getKartGFX()->setCreationRateAbsolute(KartGFX::KGFX_SKIDL, 0); m_kart->getKartGFX()->setCreationRateAbsolute(KartGFX::KGFX_SKIDR, 0); m_kart->getKartGFX()->updateSkidLight(0); - m_kart->getControls().m_skid = KartControl::SC_NONE; + m_kart->getControls().setSkidControl(KartControl::SC_NONE); btVector3 rot(0, 0, 0); // Only access the vehicle if the kart is not a ghost diff --git a/src/network/protocols/controller_events_protocol.cpp b/src/network/protocols/controller_events_protocol.cpp index f0e986b83..ca0be4d84 100644 --- a/src/network/protocols/controller_events_protocol.cpp +++ b/src/network/protocols/controller_events_protocol.cpp @@ -72,12 +72,12 @@ bool ControllerEventsProtocol::notifyEventAsynchronous(Event* event) Controller *controller = World::getWorld()->getKart(kart_id) ->getController(); KartControl *controls = controller->getControls(); - controls->m_brake = (serialized_1 & 0x40)!=0; - controls->m_nitro = (serialized_1 & 0x20)!=0; - controls->m_rescue = (serialized_1 & 0x10)!=0; - controls->m_fire = (serialized_1 & 0x08)!=0; - controls->m_look_back = (serialized_1 & 0x04)!=0; - controls->m_skid = KartControl::SkidControl(serialized_1 & 0x03); + controls->setBrake( (serialized_1 & 0x40)!=0); + controls->setNitro( (serialized_1 & 0x20)!=0); + controls->setRescue( (serialized_1 & 0x10)!=0); + controls->setFire( (serialized_1 & 0x08)!=0); + controls->setLookBack((serialized_1 & 0x04)!=0); + controls->setSkidControl(KartControl::SkidControl(serialized_1 & 0x03)); controller->action(action, action_value); } @@ -110,19 +110,19 @@ void ControllerEventsProtocol::controllerAction(Controller* controller, KartControl* controls = controller->getControls(); uint8_t serialized_1 = 0; - serialized_1 |= (controls->m_brake==true); + serialized_1 |= (controls->getBrake()==true); serialized_1 <<= 1; - serialized_1 |= (controls->m_nitro==true); + serialized_1 |= (controls->getNitro()==true); serialized_1 <<= 1; - serialized_1 |= (controls->m_rescue==true); + serialized_1 |= (controls->getRescue()==true); serialized_1 <<= 1; - serialized_1 |= (controls->m_fire==true); + serialized_1 |= (controls->getFire()==true); serialized_1 <<= 1; - serialized_1 |= (controls->m_look_back==true); + serialized_1 |= (controls->getLookBack()==true); serialized_1 <<= 2; - serialized_1 += controls->m_skid; - uint8_t serialized_2 = (uint8_t)(controls->m_accel*255.0); - uint8_t serialized_3 = (uint8_t)(controls->m_steer*127.0); + serialized_1 += controls->getSkidControl(); + uint8_t serialized_2 = (uint8_t)(controls->getAccel()*255.0); + uint8_t serialized_3 = (uint8_t)(controls->getSteer()*127.0); NetworkString *ns = getNetworkString(13); ns->addFloat(World::getWorld()->getTime()); diff --git a/src/race/history.cpp b/src/race/history.cpp index 34af74364..82f8fe79f 100644 --- a/src/race/history.cpp +++ b/src/race/history.cpp @@ -208,8 +208,8 @@ void History::Save() for(int k=0; kgetControls().m_nitro || kart->isOnMinNitroTime()) + if(kart->getControls().getNitro() || kart->isOnMinNitroTime()) m.setTexture(0, m_gauge_full_bright); else m.setTexture(0, m_gauge_full); From e56d3247381ac3b9c8411b60d22636c17170bc20 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 17 Aug 2016 08:08:42 +1000 Subject: [PATCH 118/350] Moved the kart control events from the kart control class instead of the kart rewinder. --- sources.cmake | 2 +- src/karts/controller/kart_control.cpp | 160 ++++++++++++++++++++++++++ src/karts/controller/kart_control.hpp | 41 +++---- src/karts/kart_rewinder.cpp | 25 ---- src/karts/kart_rewinder.hpp | 6 - src/network/rewind_info.hpp | 3 +- 6 files changed, 176 insertions(+), 61 deletions(-) create mode 100644 src/karts/controller/kart_control.cpp diff --git a/sources.cmake b/sources.cmake index 5b3d0c21e..0837c5629 100644 --- a/sources.cmake +++ b/sources.cmake @@ -1,5 +1,5 @@ # Modify this file to change the last-modified date when you add/remove a file. -# This will then trigger a new cmake run automatically. +# This will then trigger a new cmake run automatically. file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp") file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp") file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*") diff --git a/src/karts/controller/kart_control.cpp b/src/karts/controller/kart_control.cpp new file mode 100644 index 000000000..a32320bd6 --- /dev/null +++ b/src/karts/controller/kart_control.cpp @@ -0,0 +1,160 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2008-2016 Joerg Henrichs +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "karts/controller/kart_control.hpp" + +#include "network/rewind_manager.hpp" + + +// ------------------------------------------------------------------------ +/** Called when going back in time during a rewind. Nothing to do here + * in this case. + */ +void KartControl::undo(BareNetworkString *buffer) +{ +} // undo + +// ------------------------------------------------------------------------ +/** Replay an event for a KartControl object from the buffer. + * \param buffer BareNetworkString with saved event into. + */ +void KartControl::rewind(BareNetworkString *buffer) +{ + if(buffer->getTotalSize()>1) + { + // Full state including accel and steering was saved + setFromBuffer(buffer); + } + else // only a button event was stored + { + setButtonsCompressed(buffer->getUInt8()); + } +} // rewind + +// ------------------------------------------------------------------------ +/** Sets the current steering value. */ +void KartControl::setSteer(float f) +{ + RewindManager *re = RewindManager::get(); + if (re->isEnabled() && !re->isRewinding() && m_steer != f) + { + // Save full status + BareNetworkString *buffer = new BareNetworkString(getLength()); + copyToBuffer(buffer); + RewindManager::get()->addEvent(this, buffer); + } + m_steer = f; +} // setSteer + +// ---------------------------------------------------------------------------- +/** Sets the acceleration. */ +void KartControl::setAccel(float f) +{ + RewindManager *re = RewindManager::get(); + if (re->isEnabled() && !re->isRewinding() && m_accel != f) + { + BareNetworkString *buffer = new BareNetworkString(getLength()); + copyToBuffer(buffer); + RewindManager::get()->addEvent(this, buffer); + } + m_accel = f; +} // setAccel + +// ---------------------------------------------------------------------------- +/** Sets if the kart is braking. */ +void KartControl::setBrake(bool b) +{ + RewindManager *re = RewindManager::get(); + if (re->isEnabled() && !re->isRewinding() && m_brake != b) + { + // Only store the buttons in this case + BareNetworkString *buffer = new BareNetworkString(1); + buffer->addUInt8(getButtonsCompressed()); + RewindManager::get()->addEvent(this, buffer); + } + m_brake = b; +} // setBrake +// ---------------------------------------------------------------------------- +/** Sets if the kart activates nitro. */ +void KartControl::setNitro(bool b) +{ + RewindManager *re = RewindManager::get(); + if (re->isEnabled() && !re->isRewinding() && m_nitro != b) + { + BareNetworkString *buffer = new BareNetworkString(1); + buffer->addUInt8(getButtonsCompressed()); + RewindManager::get()->addEvent(this, buffer); + } + m_nitro = b; +} // setNitro + +// ---------------------------------------------------------------------------- +/** Sets the skid control for this kart. */ +void KartControl::setSkidControl(SkidControl sc) +{ + RewindManager *re = RewindManager::get(); + if (re->isEnabled() && !re->isRewinding() && m_skid != sc) + { + BareNetworkString *buffer = new BareNetworkString(1); + buffer->addUInt8(getButtonsCompressed()); + RewindManager::get()->addEvent(this, buffer); + } + m_skid = sc; +} // seSkidControl + +// ---------------------------------------------------------------------------- +/** Returns if this kart wants to get rescued. */ +void KartControl::setRescue(bool b) +{ + RewindManager *re = RewindManager::get(); + if (re->isEnabled() && !re->isRewinding() && m_rescue != b) + { + BareNetworkString *buffer = new BareNetworkString(1); + buffer->addUInt8(getButtonsCompressed()); + RewindManager::get()->addEvent(this, buffer); + } + m_rescue = b; +} // setRescue + +// ---------------------------------------------------------------------------- +/** Sets if the kart wants to fire. */ +void KartControl::setFire(bool b) +{ + RewindManager *re = RewindManager::get(); + if (re->isEnabled() && !re->isRewinding() && m_fire != b) + { + BareNetworkString *buffer = new BareNetworkString(1); + buffer->addUInt8(getButtonsCompressed()); + RewindManager::get()->addEvent(this, buffer); + } + m_fire = b; +} // setFire + +// ---------------------------------------------------------------------------- +/** Sets if the kart wants to look (and therefore also fires) backwards. */ +void KartControl::setLookBack(bool b) +{ + RewindManager *re = RewindManager::get(); + if (re->isEnabled() && !re->isRewinding() && m_look_back != b) + { + BareNetworkString *buffer = new BareNetworkString(1); + buffer->addUInt8(getButtonsCompressed()); + RewindManager::get()->addEvent(this, buffer); + } + m_look_back = b; +} // setLookBack diff --git a/src/karts/controller/kart_control.hpp b/src/karts/controller/kart_control.hpp index ce974f1e4..06bc8784e 100644 --- a/src/karts/controller/kart_control.hpp +++ b/src/karts/controller/kart_control.hpp @@ -20,13 +20,12 @@ #define HEADER_KART_CONTROL_HPP #include "network/network_string.hpp" - -#include +#include "network/rewind_info.hpp" /** * \ingroup controller */ -class KartControl +class KartControl : public EventRewinder { public: /** The skidding control state: SC_NONE: not pressed; @@ -52,7 +51,18 @@ private: /** True if the kart looks (and shoots) backwards. */ bool m_look_back; public: + virtual void undo(BareNetworkString *buffer); + virtual void rewind(BareNetworkString *buffer); + void setSteer(float f); + void setAccel(float f); + void setBrake(bool b); + void setNitro(bool b); + void setSkidControl(SkidControl sc); + void setRescue(bool b); + void setFire(bool b); + void setLookBack(bool b); + // ------------------------------------------------------------------------ KartControl() { reset(); @@ -134,54 +144,29 @@ public: /** Returns the current steering value. */ float getSteer() const { return m_steer; } // ------------------------------------------------------------------------ - /** Sets the current steering value. */ - void setSteer(float f) { m_steer = f; } - // ------------------------------------------------------------------------ /** Returns current acceleration. */ float getAccel() const { return m_accel; } // ------------------------------------------------------------------------ - /** Sets the acceleration. */ - void setAccel(float f) { m_accel = f; } - // ------------------------------------------------------------------------ /** Returns if the kart is braking. */ bool getBrake() const { return m_brake; } // ------------------------------------------------------------------------ - /** Sets if the kart is braking. */ - void setBrake(bool b) { m_brake = b; } - // ------------------------------------------------------------------------ /** Returns if the kart activates nitro. */ bool getNitro() const { return m_nitro; } // ------------------------------------------------------------------------ - /** Sets if the kart activates nitro. */ - void setNitro(bool b) { m_nitro = b; } - // ------------------------------------------------------------------------ /** Returns the skidding control state: SC_NONE: not pressed; * SC_NO_DIRECTION: pressed, but no steering; * SC_LEFT/RIGHT: pressed in the specified direction. */ SkidControl getSkidControl() const { return m_skid; } // ------------------------------------------------------------------------ - /** Sets the skid control for this kart. */ - void setSkidControl(SkidControl sc) { m_skid = sc; } - // ------------------------------------------------------------------------ /** Returns true if the kart triggered rescue. */ bool getRescue() const { return m_rescue; } // ------------------------------------------------------------------------ - /** Returns if this kart wants to get rescued. */ - void setRescue(bool b) { m_rescue = b; } - // ------------------------------------------------------------------------ /** Returns if fire is selected. */ bool getFire() const { return m_fire; } // ------------------------------------------------------------------------ - /** Sets if the kart wants to fire. */ - void setFire(bool b) { m_fire = b; } - // ------------------------------------------------------------------------ /** Returns if the kart wants to look back (which also implies that it * will fire backwards. */ bool getLookBack() const { return m_look_back; } - // ------------------------------------------------------------------------ - /** Sets if the kart wants to look (and therefore also fires) backwards. */ - void setLookBack(bool b) { m_look_back = b; } - }; #endif diff --git a/src/karts/kart_rewinder.cpp b/src/karts/kart_rewinder.cpp index 7ea2d6993..aa45a4151 100644 --- a/src/karts/kart_rewinder.cpp +++ b/src/karts/kart_rewinder.cpp @@ -37,7 +37,6 @@ KartRewinder::KartRewinder(AbstractKart *kart) : Rewinder(/*can_be_destroyed*/ f */ void KartRewinder::reset() { - m_previous_control = m_kart->getControls(); } // reset // ---------------------------------------------------------------------------- @@ -114,35 +113,11 @@ void KartRewinder::rewindToState(BareNetworkString *buffer) */ void KartRewinder::update() { - // Don't store events from a rewind - if(RewindManager::get()->isRewinding()) return; - - // Check if an event has happened that needs to be recorded - bool control_event = !(m_kart->getControls() == m_previous_control); - uint8_t type = (control_event ? EVENT_CONTROL : 0); - if(type == 0) - return; // no event - - // Likely it is only a single event, so limit initial memory allocation - BareNetworkString *buffer = - new BareNetworkString(m_previous_control.getLength()); - buffer->addUInt8(type); - if(control_event) - { - m_previous_control = m_kart->getControls(); - m_previous_control.copyToBuffer(buffer); - } - // The rewind manager will free the memory once it's not needed anymore - //XXXXXXXXXXXXXXX RewindManager::get()->addEvent(this, buffer); } // update // ---------------------------------------------------------------------------- void KartRewinder::rewindToEvent(BareNetworkString *buffer) { - buffer->reset(); - uint8_t type = buffer->getUInt8(); - if(type & EVENT_CONTROL) - m_kart->getControls().setFromBuffer(buffer); }; // rewindToEvent diff --git a/src/karts/kart_rewinder.hpp b/src/karts/kart_rewinder.hpp index adab48939..ae279fc2b 100644 --- a/src/karts/kart_rewinder.hpp +++ b/src/karts/kart_rewinder.hpp @@ -19,8 +19,6 @@ #ifndef HEADER_KART_REWINDER_HPP #define HEADER_KART_REWINDER_HPP -#include "karts/controller/kart_control.hpp" - #include "network/rewinder.hpp" #include "utils/cpp2011.hpp" @@ -33,10 +31,6 @@ private: /** Pointer to the original kart object. */ AbstractKart *m_kart; - /** The previous kart controls, used to detect if a new event - * needs to be created. */ - KartControl m_previous_control; - // Flags to indicate the different event types enum { EVENT_CONTROL = 0x01, EVENT_ATTACH = 0x02 }; diff --git a/src/network/rewind_info.hpp b/src/network/rewind_info.hpp index de4b033e3..4637824c0 100644 --- a/src/network/rewind_info.hpp +++ b/src/network/rewind_info.hpp @@ -195,6 +195,7 @@ public: * It calls undoEvent in the rewinder. */ virtual void undo() { + m_buffer->reset(); m_event_rewinder->undo(m_buffer); } // undo // ------------------------------------------------------------------------ @@ -205,7 +206,7 @@ public: { // Make sure to reset the buffer so we read from the beginning m_buffer->reset(); - m_event_rewinder->rewind(getBuffer()); + m_event_rewinder->rewind(m_buffer); } // rewind // ------------------------------------------------------------------------ /** Returns the buffer with the event information in it. */ From adba5c48c8bde3af3bedd886d00e99d9eb7c6aae Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 17 Aug 2016 08:32:11 +1000 Subject: [PATCH 119/350] Fix commented out code to use setter/getter in KartControl. --- src/karts/controller/skidding_ai.cpp | 4 ++-- src/karts/controller/test_ai.cpp | 4 ++-- src/karts/kart.cpp | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index d16c9e21a..11ecea74b 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -285,8 +285,8 @@ void SkiddingAI::update(float dt) // or slipstreaming. #undef AI_DOES_NOT_MOVE_FOR_DEBUGGING #ifdef AI_DOES_NOT_MOVE_FOR_DEBUGGING - m_controls->m_accel = 0; - m_controls->m_steer = 0; + m_controls->setAccel(0); + m_controls->setSteer(0); return; #endif diff --git a/src/karts/controller/test_ai.cpp b/src/karts/controller/test_ai.cpp index 49ef406ef..70f939c42 100644 --- a/src/karts/controller/test_ai.cpp +++ b/src/karts/controller/test_ai.cpp @@ -291,8 +291,8 @@ void SkiddingAI::update(float dt) // or slipstreaming. #undef AI_DOES_NOT_MOVE_FOR_DEBUGGING #ifdef AI_DOES_NOT_MOVE_FOR_DEBUGGING - m_controls->m_accel = 0; - m_controls->m_steer = 0; + m_controls->setAccel(0); + m_controls->setSteer(0); return; #endif diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index f8ae75598..1f28ec5e2 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1185,8 +1185,8 @@ void Kart::update(float dt) getVelocity().getX(), getVelocity().getY(), getVelocity().getZ(), getVelocity().length(), m_max_speed->getCurrentMaxSpeed(), - getControls().m_steer, - getControls().m_accel); + getControls().getSteer(), + getControls().getAccel()); #endif // update star effect (call will do nothing if stars are not activated) @@ -2791,7 +2791,7 @@ void Kart::updateGraphics(float dt, const Vec3& offset_xyz, } #ifdef XX // cheap wheelie effect - if (m_controls.m_nitro) + if (m_controls.getNitro()) { m_node->updateAbsolutePosition(); m_kart_model->getWheelNodes()[0]->updateAbsolutePosition(); From 3d5de1c24cccbd7f3ac960e780cbba6bc7bd84f8 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 17 Aug 2016 17:54:26 +1000 Subject: [PATCH 120/350] Fixed saving kart control events (they stored the previous value, not the new value). --- src/karts/controller/kart_control.cpp | 40 ++++++++++++++++----------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/karts/controller/kart_control.cpp b/src/karts/controller/kart_control.cpp index a32320bd6..77deb70df 100644 --- a/src/karts/controller/kart_control.cpp +++ b/src/karts/controller/kart_control.cpp @@ -50,111 +50,119 @@ void KartControl::rewind(BareNetworkString *buffer) /** Sets the current steering value. */ void KartControl::setSteer(float f) { + float old_steer = m_steer; + m_steer = f; RewindManager *re = RewindManager::get(); - if (re->isEnabled() && !re->isRewinding() && m_steer != f) + if (re->isEnabled() && !re->isRewinding() && old_steer != m_steer) { // Save full status BareNetworkString *buffer = new BareNetworkString(getLength()); copyToBuffer(buffer); RewindManager::get()->addEvent(this, buffer); } - m_steer = f; } // setSteer // ---------------------------------------------------------------------------- /** Sets the acceleration. */ void KartControl::setAccel(float f) { + float old_accel = m_steer; + m_accel = f; RewindManager *re = RewindManager::get(); - if (re->isEnabled() && !re->isRewinding() && m_accel != f) + if (re->isEnabled() && !re->isRewinding() && old_accel != m_accel) { BareNetworkString *buffer = new BareNetworkString(getLength()); copyToBuffer(buffer); RewindManager::get()->addEvent(this, buffer); } - m_accel = f; } // setAccel // ---------------------------------------------------------------------------- /** Sets if the kart is braking. */ void KartControl::setBrake(bool b) { + bool old_brake = m_brake; + m_brake = b; RewindManager *re = RewindManager::get(); - if (re->isEnabled() && !re->isRewinding() && m_brake != b) + if (re->isEnabled() && !re->isRewinding() && old_brake != m_brake) { // Only store the buttons in this case BareNetworkString *buffer = new BareNetworkString(1); buffer->addUInt8(getButtonsCompressed()); RewindManager::get()->addEvent(this, buffer); } - m_brake = b; } // setBrake // ---------------------------------------------------------------------------- /** Sets if the kart activates nitro. */ void KartControl::setNitro(bool b) { + bool old_nitro = m_nitro; + m_nitro = b; RewindManager *re = RewindManager::get(); - if (re->isEnabled() && !re->isRewinding() && m_nitro != b) + if (re->isEnabled() && !re->isRewinding() && old_nitro != m_nitro) { BareNetworkString *buffer = new BareNetworkString(1); buffer->addUInt8(getButtonsCompressed()); RewindManager::get()->addEvent(this, buffer); } - m_nitro = b; } // setNitro // ---------------------------------------------------------------------------- /** Sets the skid control for this kart. */ void KartControl::setSkidControl(SkidControl sc) { + bool old_skid = m_skid; + m_skid = sc; RewindManager *re = RewindManager::get(); - if (re->isEnabled() && !re->isRewinding() && m_skid != sc) + if (re->isEnabled() && !re->isRewinding() && old_skid != m_skid) { BareNetworkString *buffer = new BareNetworkString(1); buffer->addUInt8(getButtonsCompressed()); RewindManager::get()->addEvent(this, buffer); } - m_skid = sc; } // seSkidControl // ---------------------------------------------------------------------------- /** Returns if this kart wants to get rescued. */ void KartControl::setRescue(bool b) { + bool old_rescue = m_rescue; + m_rescue = b; RewindManager *re = RewindManager::get(); - if (re->isEnabled() && !re->isRewinding() && m_rescue != b) + if (re->isEnabled() && !re->isRewinding() && old_rescue != m_rescue) { BareNetworkString *buffer = new BareNetworkString(1); buffer->addUInt8(getButtonsCompressed()); RewindManager::get()->addEvent(this, buffer); } - m_rescue = b; } // setRescue // ---------------------------------------------------------------------------- /** Sets if the kart wants to fire. */ void KartControl::setFire(bool b) { + bool old_fire = m_fire; + m_fire = b; RewindManager *re = RewindManager::get(); - if (re->isEnabled() && !re->isRewinding() && m_fire != b) + if (re->isEnabled() && !re->isRewinding() && old_fire != m_fire) { BareNetworkString *buffer = new BareNetworkString(1); buffer->addUInt8(getButtonsCompressed()); RewindManager::get()->addEvent(this, buffer); } - m_fire = b; } // setFire // ---------------------------------------------------------------------------- /** Sets if the kart wants to look (and therefore also fires) backwards. */ void KartControl::setLookBack(bool b) { + bool old_look = m_look_back; + m_look_back = b; RewindManager *re = RewindManager::get(); - if (re->isEnabled() && !re->isRewinding() && m_look_back != b) + if (re->isEnabled() && !re->isRewinding() && old_look != m_look_back) { BareNetworkString *buffer = new BareNetworkString(1); buffer->addUInt8(getButtonsCompressed()); RewindManager::get()->addEvent(this, buffer); } - m_look_back = b; } // setLookBack From e7803fe1abc280f5fffe3c5d274d7b98baba93b8 Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 19 Aug 2016 16:38:49 +1000 Subject: [PATCH 121/350] Don't use intermediate states when replaying (only the original state), since the states after the original are potentially incorrect (due to the (later) insertion of an event that triggers the rewind). Rewinds are now much more different than previously ... wip. --- src/network/rewind_info.hpp | 3 ++ src/network/rewind_manager.cpp | 98 ++++++++++++++++++---------------- 2 files changed, 55 insertions(+), 46 deletions(-) diff --git a/src/network/rewind_info.hpp b/src/network/rewind_info.hpp index 4637824c0..d55716828 100644 --- a/src/network/rewind_info.hpp +++ b/src/network/rewind_info.hpp @@ -68,6 +68,9 @@ public: /** Returns the time at which this rewind state was saved. */ float getTime() const { return m_time; } // ------------------------------------------------------------------------ + /** Sets if this RewindInfo is confirmed or not. */ + void setConfirmed(bool b) { m_is_confirmed = b; } + // ------------------------------------------------------------------------ /** Returns if this state is confirmed. */ bool isConfirmed() const { return m_is_confirmed; } // ------------------------------------------------------------------------ diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index befd0b694..f7ee16bbe 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -283,75 +283,81 @@ void RewindManager::rewindTo(float rewind_time) // First find the state to which we need to rewind // ------------------------------------------------ - int state_index = findFirstIndex(rewind_time); + int index = findFirstIndex(rewind_time); - if(!m_rewind_info[state_index]->isState()) + if(!m_rewind_info[index]->isState()) { Log::error("RewindManager", "No state for rewind to %f, state %d.", - rewind_time, state_index); + rewind_time, index); return; } - // Then undo the states that are skipped - // ------------------------------------- - for(int i=m_rewind_info.size()-1; i>(int)state_index; i--) + // Then undo the rewind infos going backwards in time + // -------------------------------------------------- + for(int i=m_rewind_info.size()-1; i>=(int)index; i--) { m_rewind_info[i]->undo(); + + // Now all states after the time we rewind to are not confirmed + // anymore. They need to be rewritten when going forward during + // the rewind. + if(m_rewind_info[i]->isState() && + m_rewind_info[i]->getTime() > m_rewind_info[index]->getTime() ) + m_rewind_info[i]->setConfirmed(false); } // for i>state - // TODO: we need some logic here to handle that confirmed states are not - // necessarily the same for each rewinder. So if we rewind to t, one - // rewinder forces us to go back to t1 (latest state saved before t), - // another one to t2. - // So we have to detect the latest time t_min < t for which each - // rewinder has a confirmed state. Then we rewind from t_min. But if we - // find another confirmed state for a rewinder at time t1 (t_min(m_rewind_info[state_index]); - float exact_rewind_time = state->getTime(); - float local_physics_time = state->getLocalPhysicsTime(); World *world = World::getWorld(); - world->getPhysics()->getPhysicsWorld()->setLocalTime(local_physics_time); - - // Rewind all objects (and also all state) that happen at the - // current time. - // TODO: this assumes atm that all rewinder have an initial state - // for 'current_time'!!! - - // Store the time to which we have to replay to float current_time = world->getTime(); + // Get the (first) full state to which we have to rewind + RewindInfoState *state = + dynamic_cast(m_rewind_info[index]); + + // Store the time to which we have to replay to + float exact_rewind_time = state->getTime(); + + // Now start the rewind with the full state: world->setTime(exact_rewind_time); + float local_physics_time = state->getLocalPhysicsTime(); + world->getPhysics()->getPhysicsWorld()->setLocalTime(local_physics_time); - // Now go forward through the saved states, and - // replay taking the events into account. - while( world->getTime() < current_time && - state_index < (int)m_rewind_info.size() ) + // Restore all states from the current time - the full state of a race + // will be potentially stored in several state objects. State can be NULL + // if the next event is not a state + while(state && state->getTime()==exact_rewind_time) { + state->rewind(); + index++; + if(index>=m_rewind_info.size()) break; + state = dynamic_cast(m_rewind_info[index]); + } - // Now set all events and confirmed states - while(state_index < (int)m_rewind_info.size() && - m_rewind_info[state_index]->getTime()<=world->getTime()+0.001f) + // Now go forward through the list of rewind infos: + // ------------------------------------------------ + while( world->getTime() < current_time && + index < (int)m_rewind_info.size() ) + { + // Now handle all states and events at the current time before + // updating the world: + while(index < (int)m_rewind_info.size() && + m_rewind_info[index]->getTime()<=world->getTime()+0.001f) { - if(m_rewind_info[state_index]->isEvent() || - m_rewind_info[state_index]->isConfirmed() ) + if(m_rewind_info[index]->isState()) { - m_rewind_info[state_index]->rewind(); + // TOOD: replace the old state with a new state. + // For now just set it to confirmed + m_rewind_info[index]->setConfirmed(true); } - state_index++; + else if(m_rewind_info[index]->isEvent()) + { + m_rewind_info[index]->rewind(); + } + index++; } - float dt = determineTimeStepSize(state_index, current_time); + float dt = determineTimeStepSize(index, current_time); world->updateWorld(dt); #define SHOW_ROLLBACK #ifdef SHOW_ROLLBACK From 67e94e73ced5f3d3d872706f57314b77709543f9 Mon Sep 17 00:00:00 2001 From: Alpt Date: Sat, 20 Aug 2016 00:28:59 +0200 Subject: [PATCH 122/350] In the addons screen, show a tip to remember that the Internet connection is disabled. (Issue #1763). (#2605) --- data/gui/addons_screen.stkgui | 3 ++ src/states_screens/addons_screen.cpp | 59 +++++++++++++++++++++++++++- src/states_screens/addons_screen.hpp | 2 + 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/data/gui/addons_screen.stkgui b/data/gui/addons_screen.stkgui index c099036c5..6c353affe 100644 --- a/data/gui/addons_screen.stkgui +++ b/data/gui/addons_screen.stkgui @@ -33,5 +33,8 @@ + + + diff --git a/src/states_screens/addons_screen.cpp b/src/states_screens/addons_screen.cpp index 9e9db77b2..d2acfdf44 100644 --- a/src/states_screens/addons_screen.cpp +++ b/src/states_screens/addons_screen.cpp @@ -65,6 +65,7 @@ AddonsScreen::AddonsScreen() : Screen("addons_screen.stkgui") m_date_filters.push_back(filter_1y); m_date_filters.push_back(filter_2y); + m_show_tips = true; } // AddonsScreen // ---------------------------------------------------------------------------- @@ -95,7 +96,11 @@ void AddonsScreen::loadedFromFile() GUIEngine::ListWidget* w_list = getWidget("list_addons"); w_list->setColumnListener(this); - + + GUIEngine::LabelWidget* w_tips = + getWidget("tips_label"); + w_tips->setScrollSpeed(15); + } // loadedFromFile @@ -130,6 +135,26 @@ void AddonsScreen::beforeAddingWidget() { w_filter_rating->addLabel(StringUtils::toWString(n / 2.0)); } + + + GUIEngine::LabelWidget *w_tips = + getWidget("tips_label"); + bool ip = UserConfigParams::m_internet_status == RequestManager::IPERM_ALLOWED; + if(!ip) + { + w_tips->setVisible(true); + + w_tips->setText( _("Access to the Internet is disabled. " + "(To enable it, go to options and " + "select tab 'User Interface')"), + false); + + w_tips->m_properties[GUIEngine::PROP_HEIGHT] = "fit"; + calculateLayout(); + + m_show_tips = true; + } + } // ---------------------------------------------------------------------------- @@ -157,7 +182,21 @@ void AddonsScreen::init() w_list->setIcons(m_icon_bank, (int)(m_icon_height)); m_type = "kart"; + bool ip = UserConfigParams::m_internet_status == RequestManager::IPERM_ALLOWED; + if(ip) + { + // Nothing to show in the tips label, disable it. + + GUIEngine::LabelWidget *w_tips = + getWidget("tips_label"); + + w_tips->setVisible(false); + w_tips->m_properties[GUIEngine::PROP_HEIGHT] = "0"; + calculateLayout(); + m_show_tips = false; + } // ip + getWidget("reload")->setActive(ip); // Reset filter. @@ -525,4 +564,22 @@ void AddonsScreen::onUpdate(float dt) // Addons manager is still initialising/downloading. } } + + + if(m_show_tips) + { + GUIEngine::LabelWidget *w_tips = + getWidget("tips_label"); + + w_tips->update(dt); + if(w_tips->scrolledOff()) + { + // Tips have been shown once. Disable tips_label. + w_tips->setVisible(false); + w_tips->m_properties[GUIEngine::PROP_HEIGHT] = "0"; + calculateLayout(); + m_show_tips = false; + } + } + } // onUpdate diff --git a/src/states_screens/addons_screen.hpp b/src/states_screens/addons_screen.hpp index 8b4c1be5b..3b12e3254 100644 --- a/src/states_screens/addons_screen.hpp +++ b/src/states_screens/addons_screen.hpp @@ -84,6 +84,8 @@ private: /** List of date filters **/ std::vector m_date_filters; + bool m_show_tips; + public: /** Load the addons into the main list.*/ From 91e11a8d883926dfe7fec99410195b1bee4c5d1b Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 20 Aug 2016 09:01:52 +0800 Subject: [PATCH 123/350] Don't crash in overworld --- src/tracks/track_object.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/tracks/track_object.cpp b/src/tracks/track_object.cpp index 3d9abceb0..92e33b856 100644 --- a/src/tracks/track_object.cpp +++ b/src/tracks/track_object.cpp @@ -190,14 +190,16 @@ void TrackObject::init(const XMLNode &xml_node, scene::ISceneNode* parent, if (model_name.size() > 0) { mesh = irr_driver->getMesh(model_name); - assert(mesh != NULL); - unsigned int n = mesh->getMeshBufferCount(); - for (unsigned int i = 0; i < n; i++) + if (mesh != NULL) { - scene::IMeshBuffer *mb = mesh->getMeshBuffer(i); - Material* m = material_manager->getMaterialFor(mb - ->getMaterial().getTexture(0), mb); - colorizable = colorizable || m->isColorizable(); + unsigned int n = mesh->getMeshBufferCount(); + for (unsigned int i = 0; i < n; i++) + { + scene::IMeshBuffer *mb = mesh->getMeshBuffer(i); + Material* m = material_manager->getMaterialFor(mb + ->getMaterial().getTexture(0), mb); + colorizable = colorizable || m->isColorizable(); + } } } From c7d970b7455c5545c81f1f942e8d802f31c50835 Mon Sep 17 00:00:00 2001 From: Deve Date: Fri, 19 Aug 2016 00:48:12 +0200 Subject: [PATCH 124/350] Allow to set ForceLegacyDevice in graphics restrictions --- src/graphics/graphics_restrictions.cpp | 1 + src/graphics/graphics_restrictions.hpp | 1 + src/graphics/irr_driver.cpp | 28 +++++++++++++++++++++++--- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/graphics/graphics_restrictions.cpp b/src/graphics/graphics_restrictions.cpp index 8e24c2da3..cf624eafd 100644 --- a/src/graphics/graphics_restrictions.cpp +++ b/src/graphics/graphics_restrictions.cpp @@ -63,6 +63,7 @@ namespace GraphicsRestrictions "FramebufferSRGBWorking", "FramebufferSRGBCapable", "GI", + "ForceLegacyDevice" }; } // namespace Private using namespace Private; diff --git a/src/graphics/graphics_restrictions.hpp b/src/graphics/graphics_restrictions.hpp index e21230af9..0f0038459 100644 --- a/src/graphics/graphics_restrictions.hpp +++ b/src/graphics/graphics_restrictions.hpp @@ -57,6 +57,7 @@ namespace GraphicsRestrictions GR_FRAMEBUFFER_SRGB_WORKING, GR_FRAMEBUFFER_SRGB_CAPABLE, GR_GI, + GR_FORCE_LEGACY_DEVICE, GR_COUNT /** MUST be last entry. */ } ; diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 6fb6bfd5a..1bd49ab04 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -528,20 +528,40 @@ void IrrDriver::initDevice() } CVS->init(); + + bool recreate_device = false; + + // Some drivers are able to create OpenGL 3.1 context, but shader-based + // pipeline doesn't work for them. For example some radeon drivers + // support only GLSL 1.3 and it causes STK to crash. We should force to use + // fixed pipeline in this case. + if (GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_FORCE_LEGACY_DEVICE)) + { + Log::warn("irr_driver", "Driver doesn't support shader-based pipeline. " + "Re-creating device to workaround the issue."); + + params.ForceLegacyDevice = true; + recreate_device = true; + } // This is the ugly hack for intel driver on linux, which doesn't // use sRGB-capable visual, even if we request it. This causes // the screen to be darker than expected. It affects mesa 10.6 and newer. // Though we are able to force to use the proper format on mesa side by // setting WithAlphaChannel parameter. - if (!ProfileWorld::isNoGraphics() && CVS->needsSRGBCapableVisualWorkaround()) + else if (CVS->needsSRGBCapableVisualWorkaround()) { Log::warn("irr_driver", "Created visual is not sRGB-capable. " "Re-creating device to workaround the issue."); - m_device->closeDevice(); - m_device->drop(); params.WithAlphaChannel = true; + recreate_device = true; + } + + if (!ProfileWorld::isNoGraphics() && recreate_device) + { + m_device->closeDevice(); + m_device->drop(); m_device = createDeviceEx(params); @@ -549,6 +569,8 @@ void IrrDriver::initDevice() { Log::fatal("irr_driver", "Couldn't initialise irrlicht device. Quitting.\n"); } + + CVS->init(); } m_scene_manager = m_device->getSceneManager(); From a001505abd2b2a49acdeced43501a4d26c2833d4 Mon Sep 17 00:00:00 2001 From: Deve Date: Fri, 19 Aug 2016 01:17:43 +0200 Subject: [PATCH 125/350] Set ForceLegacyDevice for intel ironlake and for old radeon drivers. This hopefully will allow to run STK with these graphics cards and drivers. --- data/graphical_restrictions.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data/graphical_restrictions.xml b/data/graphical_restrictions.xml index baf96cbd0..38151363e 100644 --- a/data/graphical_restrictions.xml +++ b/data/graphical_restrictions.xml @@ -3,6 +3,7 @@ + @@ -27,5 +28,5 @@ + - From d5fe015ba7ee7d3f7393ed1bcb2fefa709468555 Mon Sep 17 00:00:00 2001 From: Deve Date: Sat, 20 Aug 2016 08:35:08 +0200 Subject: [PATCH 126/350] Fixed typo in previous commit. --- data/graphical_restrictions.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/graphical_restrictions.xml b/data/graphical_restrictions.xml index 38151363e..94d5026e4 100644 --- a/data/graphical_restrictions.xml +++ b/data/graphical_restrictions.xml @@ -28,5 +28,5 @@ - + From 150e40e4e8413b775cceca79ca795f4283dff5c7 Mon Sep 17 00:00:00 2001 From: Deve Date: Sat, 20 Aug 2016 09:31:29 +0200 Subject: [PATCH 127/350] Break the loop with glClientWaitSync also when GL_CONDITION_SATISFIED is returned. The GL_CONDITION_SATISFIED says that the sync was signaled before the timeout expired. In this case there is no reason to make another glClientWaitSync execution. --- src/graphics/stk_scene_manager.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/graphics/stk_scene_manager.cpp b/src/graphics/stk_scene_manager.cpp index 6f09a0269..12b532b2c 100644 --- a/src/graphics/stk_scene_manager.cpp +++ b/src/graphics/stk_scene_manager.cpp @@ -654,10 +654,13 @@ PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("- Sync Stall", 0xFF, 0x0, 0x0); GLenum reason = glClientWaitSync(m_sync, GL_SYNC_FLUSH_COMMANDS_BIT, 0); - while (reason != GL_ALREADY_SIGNALED) + if (reason != GL_ALREADY_SIGNALED) { - if (reason == GL_WAIT_FAILED) break; - reason = glClientWaitSync(m_sync, GL_SYNC_FLUSH_COMMANDS_BIT, 1000000); + do + { + reason = glClientWaitSync(m_sync, GL_SYNC_FLUSH_COMMANDS_BIT, 1000000); + } + while (reason == GL_TIMEOUT_EXPIRED); } glDeleteSync(m_sync); PROFILER_POP_CPU_MARKER(); From a4dfbc552f96671b38758c4a38dd748805a94cff Mon Sep 17 00:00:00 2001 From: Deve Date: Sat, 20 Aug 2016 22:47:53 +0200 Subject: [PATCH 128/350] Fixed also minimap in overworld with scale rtts parameter --- src/states_screens/race_gui_overworld.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/states_screens/race_gui_overworld.cpp b/src/states_screens/race_gui_overworld.cpp index abc03fdb1..b86102abf 100644 --- a/src/states_screens/race_gui_overworld.cpp +++ b/src/states_screens/race_gui_overworld.cpp @@ -362,6 +362,7 @@ void RaceGUIOverworld::drawGlobalMiniMap() kart_xyz= kart->getXYZ(); Vec3 draw_at; track->mapPoint2MiniMap(kart_xyz, &draw_at); + draw_at *= UserConfigParams::m_scale_rtts_factor; video::ITexture* icon = kart->getKartProperties()->getMinimapIcon(); core::rect source(core::position2di(0, 0), icon->getSize()); @@ -399,6 +400,7 @@ void RaceGUIOverworld::drawGlobalMiniMap() Vec3 draw_at; track->mapPoint2MiniMap(challenges[n].m_position, &draw_at); + draw_at *= UserConfigParams::m_scale_rtts_factor; const ChallengeData* challenge = unlock_manager->getChallengeData(challenges[n].m_challenge_id); const unsigned int val = challenge->getNumTrophies(); From 790634a914d9df7f90352e90a4dc47f712700619 Mon Sep 17 00:00:00 2001 From: Deve Date: Sat, 20 Aug 2016 23:34:12 +0200 Subject: [PATCH 129/350] Use proper size for RSM framebuffer when scale rtts parameter is set. It looks that only RSM visualization was affected. --- src/graphics/rtts.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphics/rtts.cpp b/src/graphics/rtts.cpp index 2e7efa0a4..8fbe8b11d 100644 --- a/src/graphics/rtts.cpp +++ b/src/graphics/rtts.cpp @@ -244,7 +244,7 @@ RTT::RTT(size_t width, size_t height) somevector.clear(); somevector.push_back(RSM_Color); somevector.push_back(RSM_Normal); - m_RSM = new FrameBuffer(somevector, RSM_Depth, 1024, 1024, true); + m_RSM = new FrameBuffer(somevector, RSM_Depth, shadowsize0.Width, shadowsize0.Height, true); RH_Red = generateRTT3D(GL_TEXTURE_3D, 32, 16, 32, GL_RGBA16F, GL_RGBA, GL_FLOAT); RH_Green = generateRTT3D(GL_TEXTURE_3D, 32, 16, 32, GL_RGBA16F, GL_RGBA, GL_FLOAT); From ed5a957a64ff9f044a876f6ef891aa5d8d3f5b96 Mon Sep 17 00:00:00 2001 From: Deve Date: Sat, 20 Aug 2016 23:39:31 +0200 Subject: [PATCH 130/350] Fixed typo in config.xml --- src/config/user_config.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/user_config.hpp b/src/config/user_config.hpp index a8267a858..6279211c9 100644 --- a/src/config/user_config.hpp +++ b/src/config/user_config.hpp @@ -678,7 +678,7 @@ namespace UserConfigParams "Enable Screen Space Ambient Occlusion") ); PARAM_PREFIX IntUserConfigParam m_shadows_resolution PARAM_DEFAULT( IntUserConfigParam(0, - "shadows_resoltion", &m_graphics_quality, + "shadows_resolution", &m_graphics_quality, "Shadow resolution (0 = disabled") ); PARAM_PREFIX BoolUserConfigParam m_degraded_IBL PARAM_DEFAULT(BoolUserConfigParam(true, From e62948499f7f6f71a2248e1dfefff6424f94c234 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 22 Aug 2016 18:04:06 +1000 Subject: [PATCH 131/350] Fixed compiler warning. --- src/network/rewind_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index f7ee16bbe..7f06946eb 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -283,7 +283,7 @@ void RewindManager::rewindTo(float rewind_time) // First find the state to which we need to rewind // ------------------------------------------------ - int index = findFirstIndex(rewind_time); + unsigned int index = findFirstIndex(rewind_time); if(!m_rewind_info[index]->isState()) { From 8f068c4ff078f02770d690434ff487c774cb057c Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 22 Aug 2016 18:05:14 +1000 Subject: [PATCH 132/350] Somewhat reduced debug output. --- src/karts/kart.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 1f28ec5e2..746f0e1f0 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1177,14 +1177,11 @@ void Kart::update(float dt) #ifdef DEBUG_TO_COMPARE_KART_PHYSICS // This information is useful when comparing kart physics, e.g. to // see top speed, acceleration (i.e. time to top speed) etc. - Log::verbose("physics", "%s t %f xyz %f %f %f %f v %f %f %f %f maxv %f s %f a %f", + Log::verbose("physics", "%s t %f %f xyz %f %f %f v %f %f %f s %f a %f", getIdent().c_str(), - World::getWorld()->getTime(), + World::getWorld()->getTime(), dt, getXYZ().getX(), getXYZ().getY(), getXYZ().getZ(), - getXYZ().length(), getVelocity().getX(), getVelocity().getY(), getVelocity().getZ(), - getVelocity().length(), - m_max_speed->getCurrentMaxSpeed(), getControls().getSteer(), getControls().getAccel()); #endif From 75e1f1c82b1716097ab84fd017f62dfac54dd450 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 22 Aug 2016 18:06:26 +1000 Subject: [PATCH 133/350] Bugfix, wrong variable type. --- src/karts/controller/kart_control.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/karts/controller/kart_control.cpp b/src/karts/controller/kart_control.cpp index 77deb70df..accebac1f 100644 --- a/src/karts/controller/kart_control.cpp +++ b/src/karts/controller/kart_control.cpp @@ -111,7 +111,7 @@ void KartControl::setNitro(bool b) /** Sets the skid control for this kart. */ void KartControl::setSkidControl(SkidControl sc) { - bool old_skid = m_skid; + SkidControl old_skid = m_skid; m_skid = sc; RewindManager *re = RewindManager::get(); if (re->isEnabled() && !re->isRewinding() && old_skid != m_skid) From e6b2c0f1a0a7b6296af7facb968fc8fb88a56913 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 22 Aug 2016 18:08:51 +1000 Subject: [PATCH 134/350] Fised neniry leak. --- src/network/rewind_info.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/network/rewind_info.hpp b/src/network/rewind_info.hpp index d55716828..a789a7788 100644 --- a/src/network/rewind_info.hpp +++ b/src/network/rewind_info.hpp @@ -189,7 +189,10 @@ private: public: RewindInfoEvent(float time, EventRewinder *event_rewinder, BareNetworkString *buffer, bool is_confirmed); - virtual ~RewindInfoEvent() {} + virtual ~RewindInfoEvent() + { + delete m_buffer; + } // ~RewindInfoEvent // ------------------------------------------------------------------------ virtual bool isEvent() const { return true; } From 7f4d815dde5b69becffa5bdc9b8fc0c52bfbf855 Mon Sep 17 00:00:00 2001 From: Deve Date: Mon, 22 Aug 2016 20:05:36 +0200 Subject: [PATCH 135/350] Request GLES 3.0 context and fallback to 2.0 if not available. --- .../source/Irrlicht/COGLES2Driver.cpp | 43 ++++++++++++++----- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/lib/irrlicht/source/Irrlicht/COGLES2Driver.cpp b/lib/irrlicht/source/Irrlicht/COGLES2Driver.cpp index e9b8e7efa..a7243905c 100644 --- a/lib/irrlicht/source/Irrlicht/COGLES2Driver.cpp +++ b/lib/irrlicht/source/Irrlicht/COGLES2Driver.cpp @@ -123,13 +123,6 @@ namespace video EGL_NONE, 0 #endif }; - EGLint contextAttrib[] = - { -#ifdef EGL_VERSION_1_3 - EGL_CONTEXT_CLIENT_VERSION, 2, -#endif - EGL_NONE, 0 - }; EGLConfig config; EGLint num_configs; @@ -242,11 +235,41 @@ namespace video eglBindAPI(EGL_OPENGL_ES_API); #endif os::Printer::log("Creating EglContext..."); - EglContext = eglCreateContext(EglDisplay, config, EGL_NO_CONTEXT, contextAttrib); + EglContext = EGL_NO_CONTEXT; + + if (!Params.ForceLegacyDevice) + { + os::Printer::log("Trying to create Context for OpenGL-ES3."); + + EGLint contextAttrib[] = + { + #ifdef EGL_VERSION_1_3 + EGL_CONTEXT_CLIENT_VERSION, 3, + #endif + EGL_NONE, 0 + }; + + EglContext = eglCreateContext(EglDisplay, config, EGL_NO_CONTEXT, contextAttrib); + } + if (EGL_NO_CONTEXT == EglContext) { - os::Printer::log("FAILED\n"); - os::Printer::log("Could not create Context for OpenGL-ES2 display."); + os::Printer::log("Trying to create Context for OpenGL-ES2."); + + EGLint contextAttrib[] = + { + #ifdef EGL_VERSION_1_3 + EGL_CONTEXT_CLIENT_VERSION, 2, + #endif + EGL_NONE, 0 + }; + + EglContext = eglCreateContext(EglDisplay, config, EGL_NO_CONTEXT, contextAttrib); + if (EGL_NO_CONTEXT == EglContext) + { + os::Printer::log("FAILED\n"); + os::Printer::log("Could not create Context for OpenGL-ES2 display."); + } } eglMakeCurrent(EglDisplay, EglSurface, EglSurface, EglContext); From fb226a717fac44c8c0cf17a7dea70cc70ae73a8e Mon Sep 17 00:00:00 2001 From: konstin Date: Tue, 23 Aug 2016 01:51:27 +0200 Subject: [PATCH 136/350] Proper building instructions in the readme --- INSTALL.md | 82 ------------------------ README.md | 184 ++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 155 insertions(+), 111 deletions(-) delete mode 100644 INSTALL.md diff --git a/INSTALL.md b/INSTALL.md deleted file mode 100644 index 45997998f..000000000 --- a/INSTALL.md +++ /dev/null @@ -1,82 +0,0 @@ -# SuperTuxKart Installation Instructions - -Note: If you obtained this source code from Github, you also need to download -the game assets from Sourceforge using SVN. - -`svn checkout https://svn.code.sf.net/p/supertuxkart/code/stk-assets stk-assets` - -Place the `stk-assets` folder next to the source root `stk-code` folder. -See for more information - - -## Building STK on Linux - -First, make sure that you have the following packages installed: - - * OpenGL (mesa) - * OpenAL (recommended: openal-soft-devel) - * Ogg (libogg-dev) - * Vorbis (libvorbis-dev) - * Freetype (libfreetype6-dev) - * libcurl (libcurl-devel) - * libbluetooth (bluez-devel) - * libpng (libpng-devel) - * zlib (zlib-devel) - * jpeg (libjpeg-turbo-devel) - -Ubuntu command: - -``` -sudo apt-get install build-essential 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 -``` - -Unpack the files from the tarball like this: - -``` -tar xzf supertuxkart-*.tar.gz -cd supertuxkart-* -``` - -where `*` is the version of SuperTuxkart you downloaded - eg `0.8.0`. Then: - - -Compile SuperTuxKart: - -``` -mkdir cmake_build -cd cmake_build -cmake .. -make VERBOSE=1 -j2 -``` - -To create a debug version of STK, use: - -``` -cmake .. -DCMAKE_BUILD_TYPE=Debug -``` - -To test the compilation, supertuxkart can be run from the build -directory by ./bin/supertuxkart - -To install the file, as root execute: - -``` -make install -``` - -The default install location is `/usr/local`, i.e. the data files will -be written to `/usr/local/share/games/supertuxkart`, the executable -will be copied to `/usr/local/bin`. To change the default installation -location, specify `CMAKE_INSTALL_PREFIX` when running cmake, e.g.: -`cmake .. -DCMAKE_INSTALL_PREFIX=/opt/stk` - - -## Building STK on OS X -See - - -## Building STK on Windows -See diff --git a/README.md b/README.md index cec739a98..32f6c4da6 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,7 @@ SuperTuxKart is a free kart racing game. It focuses on fun and not on realistic kart physics. Instructions can be found on the in-game help page. -The SuperTuxKart homepage can be found at: - -The official SuperTuxKart forum is at . If you need support, this would be the best place to start. - -Hope you enjoy the game. - --- The SuperTuxKart development team. +The SuperTuxKart homepage can be found at . There is also our [FAQ](https://supertuxkart.net/FAQ) and information on how get in touch with the [community](https://supertuxkart.net/Community) ## Hardware Requirements * You need a 3D graphics card. (NVIDIA GeForce 8xxx and higher, ATI Radeon HD 4xxx and higher or Intel HD 3000 and higher.) @@ -18,28 +12,6 @@ Hope you enjoy the game. * Disk space: 400MB * Ideally, you want a joystick with at least 6 buttons. -## Compiling SuperTuxKart - -### Windows -1. Install VS 2013 (or later). The free express versions work fine. -2. Download and install a source package - either a released package or from our [git/svn repositories](https://supertuxkart.net/Source_control). -3. Download the latest dependency package from [here](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart%20Dependencies/Windows/). Unzip it in the root directory, so that the dependencies directory is next to the src and data directories (if you are updating from a previous dependency package, you can delete the .dll files in the root directory, they are not needed anymore). -4. Download cmake and install it. Then start cmake-gui and select the STK root directory as 'Where is the source code', and a new directory in the root directory (next to src, data etc) as the build directory (for now I assume that this directory is called bld). -5. Click on configure. You will be asked to create the directory (yes), then for your VS version. Make sure you select the right version (be aware of the easy to confuse version numbers: VS 2013 = version 12). Click on configure, then generate. This will create the directory 'bld', and a VS solution in that directory. -6. In Visual Studio open the project file generated in the 'bld' folder. -7. Right click on the supertuxkart project in the solution explorer, and select "Set as StartUp Project". -8. Select Build->Build Solution (or press F7) to compile. - -See for more information. - -Compilation with cygwin is not officially supported, but this has been done (check with the forum for details). - -### Mac OS X -The latest information about compilation on Mac are at our site: - -### UNIX -See [`INSTALL.md`](INSTALL.md) for details. - ## License This software is released under the GNU General Public License (GPL) which can be found in the file [`COPYING`](/COPYING) in the same directory as this file. Information about the licenses for artwork are contained in `data/licenses`. @@ -51,3 +23,157 @@ STK : X right, Y up, Z forwards Blender: X right, Y forwards, Z up The exporters perform the needed transform, so in Blender you just work with XY plane as ground, and things will appear fine in STK (using XZ as ground in the code, obviously). + +## Building from source + +First, you need both the code and the assets (See for more information): + +``` +git clone https://github.com/supertuxkart/stk-code +svn checkout https://svn.code.sf.net/p/supertuxkart/code/stk-assets stk-assets +``` + +## Building on Linux + +### Dependencies + +Install the following packages: + + * OpenGL (mesa) + * OpenAL (recommended: openal-soft-devel) + * Ogg (libogg-dev) + * Vorbis (libvorbis-dev) + * Freetype (libfreetype6-dev) + * libcurl (libcurl-devel) + * libbluetooth (bluez-devel) + * libpng (libpng-devel) + * zlib (zlib-devel) + * jpeg (libjpeg-turbo-devel) + +Ubuntu command: + +``` +sudo apt-get install build-essential 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 +``` + +### Compiling + +Compile SuperTuxKart: + +``` +mkdir cmake_build +cd cmake_build +cmake .. +make -j4 +``` +STK can then be run from the build directory with `bin/supertuxkart` + +### Further options + +To create a debug version of STK, use: + +``` +cmake .. -DCMAKE_BUILD_TYPE=Debug +``` + +You can install your build system-wide: + +``` +sudo make install +``` + +The default install location is `/usr/local`, i.e. the data files will +be written to `/usr/local/share/games/supertuxkart`, the executable +will be copied to `/usr/local/bin`. To change the default installation +location, specify `CMAKE_INSTALL_PREFIX` when running cmake, e.g.: +`cmake .. -DCMAKE_INSTALL_PREFIX=/opt/stk` + + +## Windows + +1. Install VS 2013 (or later). The free express versions work fine. +2. Download and install a source package - either a released package or from our [git/svn repositories](https://supertuxkart.net/Source_control). +3. Download the latest dependency package from [here](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart%20Dependencies/Windows/). Unzip it in the root directory, so that the dependencies directory is next to the src and data directories (if you are updating from a previous dependency package, you can delete the .dll files in the root directory, they are not needed anymore). +4. Download cmake and install it. Then start cmake-gui and select the STK root directory as 'Where is the source code', and a new directory in the root directory (next to src, data etc) as the build directory (for now I assume that this directory is called bld). +5. Click on configure. You will be asked to create the directory (yes), then for your VS version. Make sure you select the right version (be aware of the easy to confuse version numbers: VS 2013 = version 12). Click on configure, then generate. This will create the directory 'bld', and a VS solution in that directory. +6. In Visual Studio open the project file generated in the 'bld' folder. +7. Right click on the supertuxkart project in the solution explorer, and select "Set as StartUp Project". +8. Select Build->Build Solution (or press F7) to compile. + +## OS X + +### Getting Started + +Install developer tools, either from the OS X Install DVD or from Apple's website. + +If you have never built anything before, you have create `/usr/local/include/` first: + +```bash +sudo mkdir -p /usr/local/include/ +``` + +Symlink the `include`-folder of OpenGL framework to `/usr/local/include/GL` (Unix programs have an easier time finding it this way): + +```bash +sudo ln -s /System/Library/Frameworks/OpenGL.framework/Versions/A/Headers/ /usr/local/include/GL +``` + +On OS X 10.9.5, you might need the following workaround: + +```bash +sudo ln -s `xcrun --show-sdk-path`/usr/include/ /usr/include +sudo ln -s `xcrun --show-sdk-path`/System/Library/Frameworks/OpenGL.framework/Headers/ /usr/local/include/OpenGL +``` + +The first link is required in order to find libcurl, the second to find opengl. + +Download pre-built dependencies from [here](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart%20Dependencies/OSX/) and put the frameworks in [hard disk root]/Library/Frameworks + +### CMake + +CMake is used to build STK. At this time CMake will not make a binary that is ready for distribution. + +You'll have to run these commands inside your stk-code directory. + +### Building + +With clang: + +``` +mkdir cmake_build +cd cmake_build +cmake .. +make +``` + +With GCC : +``` +mkdir cmake_build +cd cmake_build +cmake .. -DCMAKE_CXX_COMPILER=/usr/bin/g++ -DCMAKE_C_COMPILER=/usr/bin/gcc +make +``` + +Building on 10.10 with 10.9 compat +``` +cmake .. -DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk -DCMAKE_OSX_DEPLOYMENT_TARGET=10.9 +``` + +### Xcode + +Place an additional copy of the dependencies into `Users//Library/Frameworks`. +Then cd to your cloned stk-code directory and execute the following commands: + +```bash +mkdir xcode_build && cd xcode_build +cmake .. -GXcode +``` + +Use Finder to navigate to your stk-code/xcode_build folder and open the newly generated Xcode project (SuperTuxKart.xcodeproj). + +You can then build the project in Xcode using Product -> Build + +Note: Xcode is much less well tested than makefiles, so there may be issues when trying to use Xcode. From 4ce6fb04ea1ddae2d827063f4c0f8a7a1d569004 Mon Sep 17 00:00:00 2001 From: konstin Date: Tue, 23 Aug 2016 01:58:53 +0200 Subject: [PATCH 137/350] Remove unused documentation and tool files --- AUTHORS | 1 - data/CREDITS => CREDITS | Bin SVN-CONFIG | 34 --------- TODO.md | 30 -------- data/run_me.sh | 23 ------ doc/conventions.txt | 25 ------ tools/batch.py | 103 ------------------------- tools/nightbuilder/README | 3 - tools/nightbuilder/build.py | 52 ------------- tools/nightbuilder/config.py | 43 ----------- tools/nightbuilder/night.py | 140 ---------------------------------- tools/nightbuilder/package.py | 80 ------------------- tools/nightbuilder/send.py | 65 ---------------- tools/nightbuilder/svn.py | 93 ---------------------- tools/nightbuilder/utils.py | 100 ------------------------ tools/test.sh | 19 ----- 16 files changed, 811 deletions(-) delete mode 100644 AUTHORS rename data/CREDITS => CREDITS (100%) delete mode 100644 SVN-CONFIG delete mode 100644 TODO.md delete mode 100644 data/run_me.sh delete mode 100644 doc/conventions.txt delete mode 100644 tools/batch.py delete mode 100644 tools/nightbuilder/README delete mode 100644 tools/nightbuilder/build.py delete mode 100644 tools/nightbuilder/config.py delete mode 100644 tools/nightbuilder/night.py delete mode 100644 tools/nightbuilder/package.py delete mode 100644 tools/nightbuilder/send.py delete mode 100644 tools/nightbuilder/svn.py delete mode 100644 tools/nightbuilder/utils.py delete mode 100755 tools/test.sh diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index 9249ef33a..000000000 --- a/AUTHORS +++ /dev/null @@ -1 +0,0 @@ -See the file data/CREDITS . diff --git a/data/CREDITS b/CREDITS similarity index 100% rename from data/CREDITS rename to CREDITS diff --git a/SVN-CONFIG b/SVN-CONFIG deleted file mode 100644 index 6c37e52d1..000000000 --- a/SVN-CONFIG +++ /dev/null @@ -1,34 +0,0 @@ -# Please, make sure your SVN client uses something that matches the -# following config. Specially the autoprops part, that way new files -# will be added with the right settings. Default config for command line -# svn is in ~/.subversion/config. If you use SVN for multiple projects -# simultaneously, remember you can use different configs, at least in -# cmd line (via --config-dir and for example ~/.subversion/supertuxkart/ -# with a new config file inside). - -[miscellany] -global-ignores = *.o *.lo *.la #*# .*.rej *.rej .*~ *~ .#* ._* .DS_Store *.blend1 *.blend2 -enable-auto-props = yes - -[auto-props] -*.c = svn:eol-style=native;svn:keywords=Author Date Id Revision -*.cpp = svn:eol-style=native;svn:keywords=Author Date Id Revision -*.h = svn:eol-style=native;svn:keywords=Author Date Id Revision -*.hpp = svn:eol-style=native;svn:keywords=Author Date Id Revision -*.dsp = svn:eol-style=CRLF -*.dsw = svn:eol-style=CRLF -*.sh = svn:eol-style=native;svn:executable;svn:keywords=Author Date Id Revision -*.txt = svn:eol-style=native -*.png = svn:mime-type=image/png -*.jpg = svn:mime-type=image/jpeg -Makefile = svn:eol-style=native;svn:keywords=Author Date Id Revision -*.jpeg = svn:mime-type=image/jpeg -*.gif = svn:mime-type=image/gif -*.svg = svn:mime-type=image/svg+xml -*.htm = svn:mime-type=text/html -*.html = svn:mime-type=text/html -*.css = svn:mime-type=text/css -*.pdf = svn:mime-type=application/pdf -SConstruct = svn:eol-style=native;svn:keywords=Author Date Id Revision -*.xml = svn:eol-style=LF;svn:mime-type=text/xml -*.py = svn:eol-style=native;svn:keywords=Author Date Id Revision diff --git a/TODO.md b/TODO.md deleted file mode 100644 index ae6c1ac96..000000000 --- a/TODO.md +++ /dev/null @@ -1,30 +0,0 @@ -##TODO - -SuperTuxKart is looking for additional man power to make this -one of the best free linux games out there :) We need (in -no particular order): - - - Musicians/sound engineers - - Create additional background soundtracks - - Create sound effects - - Artists and track designer - - Create additional tracks - - Create additional artwork for tracks and background images - - Developers - - Work on network play support - - Check our bug and enhancement request tracker on - https://github.com/supertuxkart/stk-code/issues - - Extend the current web page and keep it up to date - - Testers - - For just about everything, especially different platforms and graphics cards - - Writers - - Write documentation, ranging from man page, to a description for the web, to a design document, etc... - -If you want to help the SuperTuxKart Project, please contact us on the email list: [supertuxkart-devel@lists.sourceforge.net](mailto:supertuxkart-devel@lists.sourceforge.net) - -Thanks in advance! - --- The SuperTuxKart Team - - -For details, see diff --git a/data/run_me.sh b/data/run_me.sh deleted file mode 100644 index 8414ee0dc..000000000 --- a/data/run_me.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh - -# Library directory -LIBDIR="bin" - -# If we are launching from a symlink, such as /usr/local/bin/run_supertux.sh, we need to get where -# the symlink points to -pth="`readlink $0`" - -# $pth will be empty if our start path wasnt a symlink -if [ $pth ]; then - GAMEDIR="`dirname $pth`" -else - GAMEDIR="`dirname $0`" -fi - -# Change to the game dir, and go! -cd $GAMEDIR -# export game library directory -test -n "${LIBDIR}" && export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${GAMEDIR}/${LIBDIR}" - -bin/supertuxkart $* - diff --git a/doc/conventions.txt b/doc/conventions.txt deleted file mode 100644 index 11f79b04d..000000000 --- a/doc/conventions.txt +++ /dev/null @@ -1,25 +0,0 @@ -Here are the guidelines to follow when coding SuperTuxKart: - -Filenames -========= - -The filenames should be, with underscores between words. Use the .cpp -extension for C++ implementation files and .hpp for C++ headers, .c for -C files and .h for C headers. - - -Coding style -============ - -The coding style used in Super Tux Kart can be found at -http://supertuxkart.net/Coding_Style - - -Documentation line length -========================= - -All the documentation files should have at most 80 characters, because -on some displays and on paper you often are limited to 80 characters, and -also under some conditions appendices (for example, line numbers) are -added. The exception if the player's manual, because it's long, so to -understand it better it should be limited to 65 characters. diff --git a/tools/batch.py b/tools/batch.py deleted file mode 100644 index 5cb78e0be..000000000 --- a/tools/batch.py +++ /dev/null @@ -1,103 +0,0 @@ -from matplotlib import pyplot -from os import listdir - - -def is_numeric(x): - try: - float(x) - except ValueError: - return False - return True - - -avg_lap_time = {} -avg_pos = {} -avg_speed = {} -avg_top = {} -total_rescued = {} - -tests = len(listdir('../../batch'))-1 -for file in listdir('../../batch'): - if (file == '.DS_Store'): - continue - f = open('../../batch/'+file,'r') - - - ''' - name_index = file.find('.') - kart_name = str(file[:name_index]) - first = file.find('.',name_index+1) - track_name = file[name_index+1:first] - second = file.find('.',first+1) - run = int(file[first+1:second]) - ''' - track_name = "snowmountain" - kart_names = ["gnu", "sara", "tux", "elephpant"] - - if track_name == "snowmountain": - contents = f.readlines() - ''' - contents = contents[2:contents.index("[debug ] profile: \n")-1] - content = [s for s in contents if kart_name in s] - data = [float(x) for x in content[0].split() if is_numeric(x)] - if kart_name not in avg_lap_time: - avg_lap_time[kart_name] = [] - avg_pos[kart_name] = [] - avg_speed[kart_name] = [] - avg_top[kart_name] = [] - total_rescued[kart_name] = [] - - avg_lap_time[kart_name].append(data[2]/4) - avg_pos[kart_name].append(data[1]) - avg_speed[kart_name].append(data[3]) - avg_top[kart_name].append(data[4]) - total_rescued[kart_name].append(data[7]) - ''' - - contents = contents[2:6] #TODO check if all is in here - for kart in kart_names: - content = [s for s in contents if kart in s] - data = [float(x) for x in content[0].split() if is_numeric(x)] - if kart not in avg_lap_time: - avg_lap_time[kart] = [] - avg_pos[kart] = [] - avg_speed[kart] = [] - avg_top[kart] = [] - total_rescued[kart] = [] - - avg_lap_time[kart].append(data[2]/4) - avg_pos[kart].append(data[1]) - avg_speed[kart].append(data[3]) - avg_top[kart].append(data[4]) - total_rescued[kart].append(data[7]) - -tests = len(avg_lap_time["gnu"]) -print total_rescued - - -for kart in kart_names: - print "rescues for ", kart , ": ", sum(total_rescued[kart])/tests - print "avg_lap_time for " , kart , ": " , sum(avg_lap_time[kart])/tests - print "avg_pos for " , kart , ": " , sum(avg_pos[kart])/tests - print "avg_speed for " , kart , ": " , sum(avg_speed[kart])/tests - print "avg_top for " , kart , ": " , sum(avg_top[kart])/tests - - -pyplot.subplot(2,2,1) -pyplot.plot(list(xrange(tests)),avg_pos["gnu"], "b-") -pyplot.xlabel("tests") -pyplot.ylabel("gnu") -pyplot.subplot(2,2,2) -pyplot.plot(list(xrange(tests)),avg_pos["sara"], "r-") -pyplot.xlabel("tests") -pyplot.ylabel("sara") -pyplot.subplot(2,2,3) -pyplot.plot(list(xrange(tests)),avg_pos["elephpant"], "y-") -pyplot.xlabel("tests") -pyplot.ylabel("elephpant") -pyplot.subplot(2,2,4) -pyplot.plot(list(xrange(tests)),avg_pos["tux"], "g-") -pyplot.xlabel("tests") -pyplot.ylabel("tux") - -pyplot.show() diff --git a/tools/nightbuilder/README b/tools/nightbuilder/README deleted file mode 100644 index 36181578a..000000000 --- a/tools/nightbuilder/README +++ /dev/null @@ -1,3 +0,0 @@ -configuration in config.py - -To launch, python night.py diff --git a/tools/nightbuilder/build.py b/tools/nightbuilder/build.py deleted file mode 100644 index 082eedc24..000000000 --- a/tools/nightbuilder/build.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/python -# From Supertuxkart SVN revision $Revision$ -# Copyright (C) 2012 Jean-manuel clemencon (samuncle) -# Class used to build the project -################################################################################ -from subprocess import check_output -from utils import * - -class Build: - """ - Interface for the builder - """ - - # if an error occured - __noError = True - -#------------------------------------------------------------------------------- - def __init__ (self, buildDir): - """ - Constructor of the builder class - """ - self.__buildDir = buildDir - -#------------------------------------------------------------------------------- - def make(self, job): - """ - the make command - """ - changeDir = Cdir(self.__buildDir) - - # we try to build supertuxkart - try: - check_output(["make -j" + str(job)], shell=True) - except: - self.__noError = False - del changeDir - -#------------------------------------------------------------------------------- - def clean(self): - """ - the clean command - """ - changeDir = Cdir(self.__buildDir) - check_output(["make clean"], shell=True) - del changeDir - -#------------------------------------------------------------------------------- - def noError(self): - """ - return true if no error - """ - return self.__noError diff --git a/tools/nightbuilder/config.py b/tools/nightbuilder/config.py deleted file mode 100644 index 37d95c3f8..000000000 --- a/tools/nightbuilder/config.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/python -# From Supertuxkart SVN revision $Revision$ -# Copyright (C) 2012 Jean-manuel clemencon (samuncle) - -# configuration for nightly builder - -CONFIG = { -# Directory options -"WORKINGDIR" : "/home/tux/stkalpha/supertuxkart", -"BUILDDIR" : "/home/tux/stkalpha/supertuxkart/cmake_build", -"COMPRESSDIR" : "/home/tux/stkbeta", -"LOGDIR" : "/home/tux/stkalpha", -"SCRIPTDIR" : "/home/tux/stkalpha/nightbuilder", -# directory in the server -"REMOTEDIR" : "/web/public/stknigtly", - -# Build options -"JOBMAX" : 3, - -# FTP options -"FTPHOST" : "ftp.tux.net", -"FTPUSER" : "tux", -"FTPPASS" : "hell0", - -# other -"PLATFORM" : "64", -"OS" : "lin", -"VERSION" : "svn" -} - -# arguments by default -# PLEASE Don't edit this section if you don't know what you are doing. Thanks -ARG = { -"BIN" : True, -"DATA" : True, -"CLEAN" : False, -"SEND" : True, -"FORCE" : True, -"UPDATE" : True, -"JOB" : 2, -"HELP" : False -} - diff --git a/tools/nightbuilder/night.py b/tools/nightbuilder/night.py deleted file mode 100644 index dc497aa7a..000000000 --- a/tools/nightbuilder/night.py +++ /dev/null @@ -1,140 +0,0 @@ -#!/bin/python -# From Supertuxkart SVN revision $Revision$ -# Copyright (C) 2012 Jean-manuel clemencon (samuncle) -################################################################################ - -# Script used to build nighlies - - - -import os -import sys -import getopt - -# import the configuration file -from config import * - -# import functions/class -from utils import * -from svn import * -from build import * -from package import * -from send import * - -def main(): - # error - noBuildErr = False - nosvnErr = False - isBuilt = False - # parse input FIXME The parser doesn't work - #parser(sys.argv[1:]) - # welcome message - print - print "nightly builder for supertuxkart" - print "Copyright (C) 2012 Jean-manuel clemencon (samuncle)" - print separator(COLOR.OKBLUE) - - # display help - if (ARG["HELP"]): - usage() - print separator(COLOR.OKBLUE) - print - exit() - - # svn part ----------------------------------------------------------------- - # init the svn - mysvn = Svn(CONFIG["WORKINGDIR"]) - print "current svn revision: " + str(mysvn.getLocalRevision()) - # Update the svn - if (ARG["UPDATE"]): - space = bufferedOutput("start svn update @ " + getTime()) - try: - mysvn.update() - nosvnErr = True - sys.stdout.write(COLOR.OKGREEN + space + "[DONE]" + COLOR.ENDC + "\n") - except: - sys.stdout.write(COLOR.WARNING + 74 * " " + "[FAIL]" + COLOR.ENDC + "\n") - - # If no error occured - if (nosvnErr): - print "svn updated rev: " + str(mysvn.getLastRevision()) - - # buid part ---------------------------------------------------------------- - # init the build - mybuild = Build(CONFIG["WORKINGDIR"] + "/cmake_build") - # Clean the project - if (ARG["CLEAN"]): - space = bufferedOutput("start clean @ " + getTime()) - try: - mybuild.clean() - sys.stdout.write(COLOR.OKGREEN + space + "[DONE]" + COLOR.ENDC + "\n") - except: - sys.stdout.write(COLOR.WARNING + 74 * " " + "[FAIL]" + COLOR.ENDC + "\n") - - # build the project (only if no error and the revision has changed - if (nosvnErr): - if (mysvn.getIsChanged()): - print "revision changed" - space = bufferedOutput("start compilation @ " + getTime()) - mybuild.make(ARG["JOB"]) - isBuilt = True - if (mybuild.noError()): - noBuildErr = True - sys.stdout.write(COLOR.OKGREEN + space + "[DONE]" + COLOR.ENDC + "\n") - else: - sys.stdout.write(COLOR.WARNING + 74 * " " + "[FAIL]" + COLOR.ENDC + "\n") - else: - print "revsion not changed" - - # Build the project (force) - if (ARG["FORCE"] and (not isBuilt) ): - space = bufferedOutput("start forced compilation @ " + getTime()) - mybuild.make(ARG["JOB"]) - if (mybuild.noError()): - noBuildErr = True - sys.stdout.write(COLOR.OKGREEN + space + "[DONE]" + COLOR.ENDC + "\n") - else: - sys.stdout.write(COLOR.WARNING + 74 * " " + "[FAIL]" + COLOR.ENDC + "\n") - - # package part ------------------------------------------------------------- - mypack = Package(mysvn.getLastRevision(), CONFIG["COMPRESSDIR"],CONFIG["OS"],CONFIG["PLATFORM"]) - # pack the binary - if (noBuildErr and ARG["BIN"]): - space = bufferedOutput("start bin file compress @ " + getTime()) - try: - mypack.addFile("supertuxkart", "stkbin", CONFIG["BUILDDIR"] + "/bin") - sys.stdout.write(COLOR.OKGREEN + space + "[DONE]" + COLOR.ENDC + "\n") - except: - sys.stdout.write(COLOR.WARNING + 74 * " " + "[FAIL]" + COLOR.ENDC + "\n") - - # pack the data - if(ARG["DATA"]): - space = bufferedOutput("start data file compress @ " + getTime()) - try: - mypack.addDir("data", "stkdat", CONFIG["WORKINGDIR"], "*.svn*") - sys.stdout.write(COLOR.OKGREEN + space + "[DONE]" + COLOR.ENDC + "\n") - except: - sys.stdout.write(COLOR.WARNING + 74 * " " + "[FAIL]" + COLOR.ENDC + "\n") - - # network part ------------------------------------------------------------- - if(ARG["SEND"]): - # send file by FTP - space = bufferedOutput("start file(s) transfer by FTP @ " + getTime()) - myFiles = mypack.getFiles() - mysend = Send(CONFIG["FTPHOST"],CONFIG["FTPUSER"],CONFIG["FTPPASS"],CONFIG["SCRIPTDIR"]) - for i in myFiles: - mysend.add(i, CONFIG["COMPRESSDIR"], CONFIG["REMOTEDIR"]) - #FIXME The ftp didn't throw an exception. - try: - mysend.send() - sys.stdout.write(COLOR.OKGREEN + space + "[DONE]" + COLOR.ENDC + "\n") - except: - sys.stdout.write(COLOR.WARNING + 74 * " " + "[FAIL]" + COLOR.ENDC + "\n") - - print separator(COLOR.OKBLUE) - print - -if __name__ == "__main__": - main() - - #sys.argv[1:] diff --git a/tools/nightbuilder/package.py b/tools/nightbuilder/package.py deleted file mode 100644 index 821758247..000000000 --- a/tools/nightbuilder/package.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/bin/python -# From Supertuxkart SVN revision $Revision$ -# Copyright (C) 2012 Jean-manuel clemencon (samuncle) -# Class used to package the project -################################################################################ -from subprocess import check_output -from utils import * - -class Package: - """ - Interface for the builder - """ - __revision = 0 - __listFile = [] - -#------------------------------------------------------------------------------- - def __init__ (self, revision, compressDir, os,platform): - """ - Constructor of the builder class - """ - self.__os = os - self.__revision = str(revision) - self.__platform = platform - self.__compressDir = compressDir - -#------------------------------------------------------------------------------- - def addFile(self, inFile, outFile, workingDir): - """ - Packing a file - """ - # filename - name = outFile \ - + "_" + self.__os \ - + self.__platform \ - + "_" + self.__revision \ - + ".zip" - self.__listFile.append(name) - - # prepare the command - command = "zip " \ - + self.__compressDir \ - + "/" + name \ - + " " + inFile - - # execute the command to pack the binary - changeDir = Cdir(workingDir) - check_output([command], shell=True) - del changeDir - -#------------------------------------------------------------------------------- - def addDir(self, inFile, outFile, workingDir, exclude): - """ - Packing a directory - """ - # filename - name = outFile \ - + "_" + self.__os \ - + "_" + self.__revision \ - + ".zip" - self.__listFile.append(name) - - # prepare the command - command = "zip -r --exclude=" \ - + exclude \ - + " " +self.__compressDir \ - + "/" + name \ - + " " + inFile - - # execute the command to pack the binary - changeDir = Cdir(workingDir) - check_output([command], shell=True) - del changeDir - -#------------------------------------------------------------------------------- - def getFiles(self): - """ - Return the list of file/directory added - """ - return self.__listFile - diff --git a/tools/nightbuilder/send.py b/tools/nightbuilder/send.py deleted file mode 100644 index 7f2613a0e..000000000 --- a/tools/nightbuilder/send.py +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/python -# From Supertuxkart SVN revision $Revision$ -# Copyright (C) 2012 Jean-manuel clemencon (samuncle) -# Class used to package the project -################################################################################ -from subprocess import check_output -import os -from utils import * - -class Send: - """ - Interface for the network - """ - - def __init__ (self, ftpHost, ftpUser, ftpPass, ftpFileDir): - """ - Constructor of the builder class - """ - - # Get the configuration for the FTP connection - lines = [ - 'FTP_HOST="' + ftpHost + '"\n', - 'FTP_USER="' + ftpUser + '"\n', - 'FTP_PASS="' + ftpPass + '"\n', - '\n', - 'ftp -n -p $FTP_HOST << EOF\n', - 'user $FTP_USER $FTP_PASS\n' - ] - - # open the ftp script - self.__ftpCmd = open("ftp.sh", "w") - self.__ftpCmd.writelines(lines) - self.__ftpFileDir = ftpFileDir - - def add(self, filename, localDir, remoteDir): - """ - Add a file to the sender - """ - command = "put " \ - + localDir \ - + "/" + filename \ - + " " + remoteDir \ - + "/" + filename \ - + "\n" - self.__ftpCmd.write(command) - - - def send(self): - """ - Send files previously added - """ - self.__ftpCmd.write("bye\nEOF\n") - self.__ftpCmd.close() - check_output([self.__ftpFileDir+"/ftp.sh"], shell=True) - #os.system("./ftp.sh") - - def alert(self): - """ - Send an e-mail alert to the mailing list - """ - #TODO - - - - diff --git a/tools/nightbuilder/svn.py b/tools/nightbuilder/svn.py deleted file mode 100644 index 6d357c81e..000000000 --- a/tools/nightbuilder/svn.py +++ /dev/null @@ -1,93 +0,0 @@ -#!/bin/python -# From Supertuxkart SVN revision $Revision$ -# Copyright (C) 2012 Jean-manuel clemencon (samuncle) -# Class for the SVN -################################################################################ - -import os -from subprocess import check_output -from utils import * - -class Svn: - """ - Interface for the SVN - """ - - __svnInfoLocal = "" # info of the local repo - __svnInfoLast = "" # info when it's updated - __localRev = 0 # the local revision number - __lastRev = 0 # the updated revision number - __isUp = False # is the repo has been already updated - -#------------------------------------------------------------------------------- - def __init__ (self, workingDir): - """ - Constructor of the svn class - """ - # local copy for the configuration - self.__workingDir = workingDir - - # cd to the working dir and get info from current revision - changeDir = Cdir(self.__workingDir) - self.__svnInfoLocal = check_output(["svn info"], shell=True) - del changeDir - - # exctract the local revision - self.__localRev = self.__parseInfo(self.__svnInfoLocal) - -#------------------------------------------------------------------------------- - def __parseInfo(self, strIn): - """ - Parse info to extract the revision - """ - workingData = strIn.split("\n") - for i in workingData: - if "Revision: " in i: - return int(i.split("Revision: ")[1]) - -#------------------------------------------------------------------------------- - def update(self): - """ - update the repository (svn up) - """ - # cd to the directory and update the svn - changeDir = Cdir(self.__workingDir) - self.__svnInfoLocal = check_output(["svn up"], shell=True) - self.__svnInfoLast = check_output(["svn info"], shell=True) - del changeDir - - # exctract the last revision - self.__lastRev = self.__parseInfo(self.__svnInfoLast) - - # now we have updated the SVN - self.__isUp = True - -#------------------------------------------------------------------------------- - def getLocalRevision(self): - """ - return the local revision - """ - return self.__localRev - -#------------------------------------------------------------------------------- - def getLastRevision(self): - """ - return the last revision - """ - if not (self.__isUp): - raise Exception ("The revision has not been updated.") - return self.__lastRev - -#------------------------------------------------------------------------------- - def getIsChanged(self): - """ - return true if revision has changed - """ - if not (self.__isUp): - raise Exception ("The revision has not been updated.") - - if (self.__lastRev != self.__localRev): - return True - else: - return False - diff --git a/tools/nightbuilder/utils.py b/tools/nightbuilder/utils.py deleted file mode 100644 index 5443af3c3..000000000 --- a/tools/nightbuilder/utils.py +++ /dev/null @@ -1,100 +0,0 @@ -#!/bin/python -# From Supertuxkart SVN revision $Revision$ -# Copyright (C) 2012 Jean-manuel clemencon (samuncle) -################################################################################ - -import os -import sys -from time import gmtime, strftime - -# import config -from config import * - - -class Cdir: - """ - A class used to change the directory and reset it when it's destructed - """ - -#------------------------------------------------------------------------------- - def __init__ (self, path): - self.oriPath = os.getcwd() - os.chdir(path) - -#------------------------------------------------------------------------------- - def __del__ (self): - os.chdir(self.oriPath) - -class COLOR: - HEADER = '\033[95m' - OKBLUE = '\033[94m' - OKGREEN = '\033[92m' - WARNING = '\033[93m' - FAIL = '\033[91m' - ENDC = '\033[0m' - -def separator(color): - return color + 80 * "-" + COLOR.ENDC - -#------------------------------------------------------------------------------- -# usage of the script. Displayed if -h is invoqued -def usage(error = ""): - if (error): - print "[error] " + error - h = [ - " Options avaliables:", - " --bin # package the binary", - " --data # package the data", - " --clean # remove all packages and logs", - " --send # send the package via FTP", - " --force # force the build (even the revision hasn't changed)", - " --update # update the SVN", - " --web # html output" - " --job= # like -j for make", - " --help # display help", - ] - for i in h: - print i - -def getTime(): - return strftime("%a, %d %b %Y %H:%M:%S GMT+01", gmtime()) - -#------------------------------------------------------------------------------- -# Used to format output -def bufferedOutput(string, nb = 74): - space = (nb - len(string)) * " " - sys.stdout.write(string) - sys.stdout.flush() - return space - -#------------------------------------------------------------------------------- -def parser(argv): - a = os.system("ls") - print a - try: - opts, args = getopt.getopt(argv, "bdcsfuhj:", ["bin", - "data", - "clean", - "send", - "force", - "update", - "help", - "job=" - ]) - for opt, args in opts: - if opt in ("-h", "--help"): - ARG["HELP"] = True - """ - if opt in ("-b", "bin"): - ARG["BIN"] = True - if opt in ("-d", "data"): - ARG["DATA"] = True - if opt in ("-s", "send"): - ARG["SEND"] = True - if opt in ("-f", "force"): - ARG["FORCE"] = True - if opt in ("-u", "update"): - ARG["UPDATE"] = True - """ - except: - usage("unrecognized option") diff --git a/tools/test.sh b/tools/test.sh deleted file mode 100755 index d46ab8117..000000000 --- a/tools/test.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -mkdir -p ../../batch -#tracks='snowmountain city lighthouse olivermath hacienda startrack farm zengarden' -#karts='gnu tux sara elephpant' -laps=4 - -#for track in $tracks; do - #for kart in $karts; do - for run in {901..1500}; do - for lap in $laps; do - ./../cmake_build/bin/supertuxkart.app/Contents/MacOS/supertuxkart -R --mode=3 --numkarts=4 --track=snowmountain --with-profile --profile-laps=4 --kart=gnu --ai=sara,tux,elephpant --no-graphics > /dev/null - #./cmake_build/bin/supertuxkart.app/Contents/MacOS/supertuxkart -R --mode=3 --numkarts=4 --track=$track --with-profile --profile-laps=$lap --kart=$kart --ai=beastie,beastie,beastie --no-graphics > /dev/null - #grep "profile" ~/Library/Application\ Support/SuperTuxKart/stdout.log > ../batch/$kart.$track.$run.txt - grep "profile" ~/Library/Application\ Support/SuperTuxKart/stdout.log > ../../batch/faceoff.$run.txt - done - done -# done -#done \ No newline at end of file From 138daf57a45b0b6923e2f2d673552966ee70449f Mon Sep 17 00:00:00 2001 From: konstin Date: Tue, 23 Aug 2016 11:07:13 +0200 Subject: [PATCH 138/350] Move credits file back --- CREDITS => data/CREDITS | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename CREDITS => data/CREDITS (100%) diff --git a/CREDITS b/data/CREDITS similarity index 100% rename from CREDITS rename to data/CREDITS From 38e57991672b06f9b6e5cfcf5d3557c143f19b88 Mon Sep 17 00:00:00 2001 From: Aapo Rantalainen Date: Wed, 24 Aug 2016 02:36:16 +0300 Subject: [PATCH 139/350] Four help?.stkgui -files were treated as binary by git (#2612) --- data/gui/help1.stkgui | Bin 7162 -> 3580 bytes data/gui/help2.stkgui | Bin 8682 -> 4340 bytes data/gui/help3.stkgui | Bin 8564 -> 4281 bytes data/gui/help4.stkgui | Bin 5858 -> 2928 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/gui/help1.stkgui b/data/gui/help1.stkgui index 7983bb68bd7f90d1b792dfdd33daed69a10d4cc0..d0ce262b6fb2a680b1d0113f48161afebb94e118 100644 GIT binary patch literal 3580 zcmcgvQE%fm41T{~!5J717}lijc0~sycNhxnRt&d2EW@y;K(-lMh$0D+TqpPIN6L0$ zr@k#$piP0sb|mT}`F$k4_^{t8ctq!=(aR*AT_=D#H-*&YGWl!u%T;=te0cw6;lsA9 z<@+~4pG6@bVNa{SP2kXeHL?iRGWqF_9#tsIDs(qW9866&bVQK3P*=`03AA>itqMhf z4up~=0mxAYbP06!?ykvRz&#oGv$p6~_5L^E0{n`~wm+=L8|MSd-in+o_i)m8N3-d5 zLJOlW5knA}2U*bZB4BCU;p9Zh2zFw>KANG;?EDQqA9_8;ae@&Kkh#%USsjAW@f<5k zOpp81o4dh*e8cOP2?cx}oXEFpCv;(UGpozwh?))epNI7B_hqsY8AweID+@c+bsxHq zsL=_Ji6gz}ZWW-+t>c@*f z85>Adel4}F!*h{+s#TCzmUKkA7-VoUXVwl`oS8jXXRINmMKPa1UNeg)r8qDUInjxx zV>U_V$oqH0)8pC!p?x4(uIgZ%B%*bay;m~dk^zSh46q9jD!M){8*66Z+$B)PT;`J0 zfli~h!$=URd*>@c_}S>tRid#X;<2oXFO;KO%6lIVzCOvcl$$ogXHHBwax(}jT~BSu z>MT<@O6Kk9v6zMC-4y0-Ho=tEi6qc(LmCS&!#lsB#AxY&z zgU2W@2YKxrjYOcxkOH&mPNpu;`!hjDE9@#V&uPPdh|rF10xpO(_LlNGV@t>4OmUo74- z+0WF&zwz+uE8#4S2u^#Tp*O0ELdS}V4v*T_;(WxX(fl|BaFHCZRw5%2jfak|Jq~?o zh0_l)-@$#SK?a>k%^vuu(u_n#5}Q+gC^->6kKOP`JNG@Q(ifv$%&FtHqscI-PX_jk f-VNf*2>DGfV)VafH@}CSrW0P*>uNFYU*-MXR87&{ z z{Rpk4jJ5SQxs;#e7`RfJvJM>;k zuce30?4CcZZI){nJxZ^w^FW&i;|l0=#_loJ!FZVIQ(O;iHpY57t=2h3w6(9$TUWrKK$;`#M zYU(jWeO~K(#slqlB<+#oGj}yVcCpo|&XoJABW{t^Xz-RcTP>htAFJTJ(?_?^@7e78 zv(VRjw9p-FLnP|SpNOpD(QWh^{$Yd#c~fgL-D50$vqwu!5JyCLi=`g!O1xp#KX&H5 z!K{VqatsZOkC=z+BCLztbgU(wwPEo9>srDK_mIPt5`TTQ%=UH!d+{G1?aqqr8bqAP znI^{pW0_S9;QMX((~i)r9*ix?oy6}7o;5^^oJk9u!_vf<+Zr7!jarVN)olG7LG5^5 zm9~jzwrT%yR$29Uv^Og(R!O;~$)L`&>te!?y6O4r+J=AOTB>!^nrIWBMx7kXKdd@> zDVFce`0)2GHD%K=R!`)MIlS2uQO8XlIj!rVrY81&*BKbG1N;h&sC8aoE!d-}09AII)ktXq|hNy$xGjd+tTj zDml-n{ltXF@Tbp=0V1WW{|+}h+7Tf|x5Lhk9}(XZMEMAjn|f?2pChw3BMs{UL;r1@ z{Qo(^YSqK$h!XZCT22m_CCm>^K6*NNkh6rH83o{%d(QB(P;BRkgY$vaZ``AkTZrFR z_*phh(3|xk`!AGTO0?N|Pn1BEpQ4rdNA@94laqL^#r=5XMl!FnxK)|txqyn2ymf-R zKQQAQSDu5ARi|i)>_v7OqP@}?SX%| z8Kb}L&8C=_tKvz($yQv6eE8|{(QLQuBW^I~h$>&<&gJsiFv!q}XN-414(r?=t$+lC7-FFk|E+t38kK3iB zyOT@hg&@!1%UgJNq!ktY@eDRFWZ%p^K8~lxhWYes1%K0@M0=iHQU*_Yxu-F%vZ!;8 ziXL8XpXUCgElqVmlw`}r1+8ZPR_Z@);k(H*{E2`@?}$5!_lB|Fhp!&${G zF)3;|P;%wD&1h@y_8Rs)U&Fws&T#wKtv@j9={=Vw{bPPOak6LG8CJsb;Lh}?o!RMi zhJi7b=!0j4BT->_=6S8{<|&A2@StVu`OXX`hv0AUd_U?#Kc3u(3y4yyanX6NRb0og znooE0r#H8I3x+RY{47JlC*HDhIk!wzy1g>0&URi0!^7ucdi(nLIFuPDY~MNyzbg@cpNd? z%lW{bd@C(mi#Je~!6NRTn~>}t!!t}u!6u!%iyqu}Eseu)k4%NkS;yr9$?**_a*g1! z8j-ftIZk~eBqyo$5~@uF1cSUL$=M1pkb~|nH)MoblIf}nLkr#rRRyzG$KK`>fbjpr zrDRKfen<|ga{xbF6I8##DWK#V>}iUakNha5cHDWgV#VYLsm482-=Svlkg1wGf|>&^ zVC5k1VY76Xf`gGABCzL&Vw>_m()vTzcL@vyZ#oI-Qlt&`CiD@J36Ufbp?{%C8$iT( z00JtxfX1bOQRrTR@#hEQE4;W%e$gERAY&?Yr%F`4UO>R`$X(IHBJKuZkJy*35eW~C z!>oc2vfTWm1qyjxqI*WG3$;RuJz8yS&rXDX z;Zlm;^~{{V;)g>B7RNCJd9B3)0;AVK%~8eBape};w!A`iAw~sL_(@W~5+#pkEJhd- z_wTS7c+v7RXFc`&FKLV{+7fQ(1^D})*{5&rjVS+y=7_l^_s7D*7NL+sbBdk^hsjag zVA~+&o<&kXR5&fvq`A?AhjGpa25`bH5`DpOOclK@{t4j#E}+|3k%Q z6!RS#QslRddH-sL^9dN0y7~P+^oxUP*c0L*97l1yfWY6OlMe46o(|7p+|a}4li?M{ EKUYNJX#fBK literal 8682 zcmeI2+fE}#5QghIPccHuU4VroD>fooiAW)0l8{0Eq)fWdBqT1ISoaLFJb?r`^T~{-Him6r~Yi20^O3mJ;z3TjAzM$RJuAII| z&#;qA;5eOpn&yy0lfOMl$N6K$tm5E10rp^H#rs-Qg4>CK~d3NSXXIE&Of5Mmv z`pCu+zO6k)U5vWCwtqeJNjgcbI_;$*TyEPb;=NnHv@czMTCM|We5VD-;! zdGEFBF!8b}4m=)Vhl>!lFWU@Xi$9y^i+eiPOj@`S9Zrh>jniW7^F7$B`*`&9sJPst zLZ>G@kRL4b%Z1f_*E{Kg2#0s@h_T#v9qD}abso<>+K%n3ds=0pM>K`*L{B+Zkq^G5 zlqRlH1Pcm8FnA#@4D~tH-PFFj*UdxOXAeoPL%!1XeOb!OwbRx?Up$ne`KPYqgn)W> zvP=(|r|_zeX`{*WYjewMd+Qhvsvs*o&`@&RPI0uS|45FAH&Sek)!ELL$C}K2Gizz` zxLf5cZ61C6sXGw&gWt!jr@a$dO5`&2$a+wtd6;BSND4unm0+r*c6t+s&2~HgRTSvLq*66t&|lYkSC5_~Y?|eCWE;LHQ!AB3JfA)888BZ(|*_HW3&eo=lc} z#Zg-bu~#Vz91F|j4ml=}9eE_lHuUeJ33WR@3p&tu3-PE#H^}w9whS`*b~YFm(9R`d zJaVOZnH4!Joeq4_I$nf-tzYZ$;y~!k@+#~=sC5e=7o%Rn&(IOW*Iz3C@(j#Z;n}HC z7>n8<>WD?z7M699Y>kY@_afSOWXjPhva?%pR4T%(qlj0(tFsmQcHU*{OUt0*CD=-q99`=L6yzYTG`o(pO8Hr;2>gh9{Ey{F^Z5Z&Q3p|VW<9ddTC>$NZ5c3l0{v-%sY z!Taa;SRmc0IO?gtqcQ;N^o~AntQUG87a55AghlUFR_Ckf!O?-iw{&!MPxRlh&lx^- z--tavP3Nd9-pQ@O`|^yXo24di*_1y*Iz8|C2rKqPNdcCx9#e4TS%;!hZqb_w70|e*X|&oH{bz RJO{mYA07_rE7X65@ed!$ diff --git a/data/gui/help3.stkgui b/data/gui/help3.stkgui index 9eb49aa110733b48668779686ae52bf7d45e551a..02bb33ab54e828654ad385e935fe37b4475c8918 100644 GIT binary patch literal 4281 zcmdT|(Qex|6n*bk+zQyskl1mu6{%sjScf((h7LvIVvhzT(Gp>b)JQ6}`}I4Ql58dO z03$Dh{a{%#d3nim&*7nF@3)nvN9nw>W}Zw&R|!dz*<6`oo_t??x}3a8-oJY_^I=^y z>fI~CXO^o++M@MO3GLd;M&+TLCvR`@sFbQGLwBRa&NjHg5J6=^Upl)Hx`euTw^xW4Ph7oUIbZKw>wN?!WmRzyWEFCXP*2uo)2gJG>=0XaR8NBbD1_FSQE#o zMIk5q{*&w5!GL_j?dJ&=d>ov})=MW$ZmUskie#TP2kt*#CbxgilZ8l08BAE$R7unH zOLr%YbVBo-`wo1a9n0$aq^vel)YkZyCiGX^{iK9$PD-eP-erZN`qLTC#LHtQc z{c=)LwhmyGUn)~K;iZ{ z$s+V4@wH{^7ek?x1GfHW`*!{BWzlHi=pL#zrQfusRNIZma^NEK3-XqrUs*{VI|-rz z=~_$5G^0d~mtr^@yF9{90-40bamv_?i}_!VSpPg6+961xrq5*5$E`uuVu> zS%+t=6$mmq8;E<|K2wZ8S!Q5FK}Rz)rTfN2FgySP34ur^FG%>dk??`mY-2V+nNmr) z@_#ol5Gy5fVyf;aaN)2%WVT^TM5PLzen@9W^g(Wfg9a3qYXv+0%AvG6YEzp-8@rGp z){4}sQU>M*#bWLGkpIR9r`EDH8fRJ_H}ud>fJf`@HiY&zgY%A#=S)F+h%=bdcfb&pIr0^fTQ|Opyi6G#W$=huT3DEi zv%yC~fzBJBk3~Tx_%fxB+ge)(t>l9e1lH0@L2?xi>UGZ7aW7xwSjQX9>F+B48SHLHbLz5MXc9~m#oJAflYKL;M4#p|+d= literal 8564 zcmeI2TTdfL5QXbGzhV|CFHv9@NLD0rvPz=8plG8Ayvn0xn*~N|46zNE{qaf8*EN;7 zFgA{2`N=}UgqaGr)&K8Umbg8bGx#UWp-yDZDLc)?b^ongYDS9?b!=`W*QmW zQ1{O?I<|{6`oX&P#&$L9rRKb{AMFe4==qs_(u`Zpa+9=n%o1!wDMUR!w+@)j^rJ;j~VmAf>YUYlb-16YHq(u=0szez0=*1#*h?G^ph@BVm;6z@=?#H^q z2UnUsw_eK(*Bm>4Q24BFUAd#|+M*q3+re@L>^ZT!t91|$v-GW84?erldiGnrbD(Hz zUtx@de0<{({#-gHk~&n(_tMo^k#sFY?P-TV%1#9XVdSiDV^Mhs!k+jO6Jz<)c|9yy`8lkl;MuMU!?`d;l@c;s<3#7LX3?qy?Nt3dB` z;~x5>rhR=LI$WcNuH+jsQP0j5S)+~n=&kmL2+Qn!VaarjvGmOvJ$0=(BFlR$^>j7X z9Z zA{P11XqvV4zss-I;uFb*9}rV;$`gIU)xOvDPr6H-;{kXn7+|mb+ec;puXSgtv8>=< zL)l4OS8FHFz%<|keQ^)gF_etJQYP&nE0dga{r9zA*|UXNDf~rtaxAsyrgGjEabBt6 znPtsc6$e|SIg9;&Nb4%=J74JvX?a2tDEmcm?^zpO0LI9R@sVtzeD$Bb{-w z;xq|v&yvi$#4RrLeJZ5o39Cp1dF(yXp6Pp6IL(u;$i`J<^Ms#MK_j+M0XeTs5{@Gq z`Dp^=o~JRN7reqx-+NmW%LC0?AN8?WHRAsAy4mF0TO4n)V!lzVM-7Z>m8E(Xof%Ml zBGgj30~u5}Kk-uu9V66PDri3;F;GP%rZbfZ!O@L@-9#-0Z`8A?{V5&P<6K&D$wG2e zRk8yt+%rzd#sci}OaPzAbq(G>>q+?gp00n^^?lOqJb<2xqnZAMuZp?IsaNf~A3W;a zZh9Haj=Ta-$2#PB=LYM^HrLzAubo(T&NxglMcsjcRXZBn?6Jx0ai}OiRh*YQ0p7ei z!->fq@3c#fEXOUKZpRYeAMiwRm zJFv_%{qZ&khKgJm$a?BdNByZ)`gHB@KB7yve&1HRY+q^JyrpnnU+r^wg>LI4dru9I z;YaVX(cg~K!-KBj%+ARBx_YZ!iC)|9y3SKy{ybrcJ%F*Zi#*}z;=~dRzJ6{;D_sQU zUPwQBf&c5WV+T3g$e%c9jE zUJ!J+1n@VkMiyM>(I#boHv+IPqJTX_vDrZNk z(ggryYy!K4cKIIekT38C3BI-syXSWEmvVvr&m@2bq1NGGy-IOy2F&PVN%C z7}pXp1eJQyIm)ZRg>~D*A|`^MR-5@g4T5Fy_jo>B^)ZeEj95Tt*4(CT2-d_pjVgGu zkDuH>3<}DPygy6O@NsY|Th5&_xvfWyDUv;EN!(vglZP*}nPyM% z5q;%~vk+N&H5@*+cD{=C(X+i)Vx`!+N7R*zR1O**3I=DTwV;S1Y30okXT^^rCwkw# zT4;ylV52YwtkSMmrU62eo+NJ`cag;+jz8QkVkIiZBGEzW__2YyEW zq5Ykh?K_;^dfyb|9Z+#P#{SuD1Va)1a7apV9*YM+q{88MkWkLPhrK}F>*+on_s8dd E0UnTWbN~PV literal 5858 zcmeI0T~8ZV5QgWvzhZ?{kxGRG+C)^M%~eBDmE43XmlihWqZm8#2bli&w(oPs!`VGv z8>dCww6b9BJ!fa$`Fdy0{`Kdfy|uaB*uW~gwZ4sQVple@q5WuEwr9KcO4mv&L+k1O znO28(kyd}R6MJVn+Vw_z-r7(0l^yE&sa|C8?KM16f`f*s0wM*=(`mR!p_KrT*y9^R;;hm zGiY)T^pfS%w1yv=_zvV9$H$KO0>MXN`JA3O(YLw%Dw$)gRrZVS4zz}(c%rjb$L1}%@O(ih0mJkazu&RstB}2Fk3*($=w~ zc!s}}jY*QP-WAs~DssBY z2j+uyyqx#3PZhaQ}^Dk^}4f zpE>ZKS@%LX`_gfjqH-+^ffO6)pF^$C@9)z-Ja?vRTT`r?; z&Pz>f`-^F*#OC)m)xgBj)raUlR=1@l<|Ch?R%b)lQeNvb!*Qyhs0_4}GsBn5ug@6o zl#iScruJ>hZl-R}&Ve+M_kDe+<%zC7iIZbq+g_Pf>TkD_#PmwFyig@mA%3zb831oy zXq~Ra{C1X}fe$PDU3q+;@F49vt#}2758xT_f|7FryfM=MT(RT=yO%LD!;^zvue;iD zl%92-3Jv@U%Y6DgmqeyRCb8gbr1x|O|G;vvVEAdOy-ao?v9uP=$65_`!K0If6(fD) z#1?s1T1TB%@=Ra0(^EK!oXO)8F%U6?5tzz6R-E&gW${LM$WIt}sf_WdcEM2R`mhVj zKuPSmj&whCPm;E<8azZZb3R_gANT;TIp2HaO7!Fmc90ptv`m%3g=1mzb4w)zo++!k zO7^vLrFSN})GXN%Q6Y1f>$Ap5iduB$O5OnzrwjJXl&|C-Sz7l47|BR{#00!*Mu;kB zR`4#+J}KmV|LtBut;NZ!qkQ)hcb)_{W%2*}gr6<{U7z@*Dt4Z$vm@u4I`2mfm#j$# zsrQ%IN9Dl(%PbBzrV~jW9*f3-Ubh zrOY2ZUtW3NT7MJRoNQmGH-5|S>|)|=XGMCBpFis-f>oX9@i$4`ls(t#b@2w!-zoc> ZU;kcC#PQxYDzknr2ze#DgRp%2{5QTKa&Z6v From a07a8b6516825084f88d16f6afa04642fd3cbabc Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Tue, 23 Aug 2016 19:53:41 -0400 Subject: [PATCH 140/350] Fix XML files and regenerate strings --- data/gui/help1.stkgui | 2 +- data/gui/help2.stkgui | 2 +- data/gui/help3.stkgui | 2 +- data/gui/help4.stkgui | 2 +- data/po/supertuxkart.pot | 146 ++++++++++++++++++++++----------------- 5 files changed, 86 insertions(+), 68 deletions(-) diff --git a/data/gui/help1.stkgui b/data/gui/help1.stkgui index d0ce262b6..7db3d345b 100644 --- a/data/gui/help1.stkgui +++ b/data/gui/help1.stkgui @@ -1,4 +1,4 @@ - +
diff --git a/data/gui/help2.stkgui b/data/gui/help2.stkgui index 6a0402dde..688b876ef 100644 --- a/data/gui/help2.stkgui +++ b/data/gui/help2.stkgui @@ -1,4 +1,4 @@ - +
diff --git a/data/gui/help3.stkgui b/data/gui/help3.stkgui index 02bb33ab5..100226da6 100644 --- a/data/gui/help3.stkgui +++ b/data/gui/help3.stkgui @@ -1,4 +1,4 @@ - +
diff --git a/data/gui/help4.stkgui b/data/gui/help4.stkgui index 8b2908be7..5d090b883 100644 --- a/data/gui/help4.stkgui +++ b/data/gui/help4.stkgui @@ -1,4 +1,4 @@ - +
diff --git a/data/po/supertuxkart.pot b/data/po/supertuxkart.pot index 6d7bd51c4..d7f7c3f7b 100644 --- a/data/po/supertuxkart.pot +++ b/data/po/supertuxkart.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: supertuxkart\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-05-30 18:52-0400\n" +"POT-Creation-Date: 2016-08-23 19:52-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -437,7 +437,7 @@ msgstr "" #. I18N: ./data/gui/user_screen_tab.stkgui #. I18N: In the user screen #: src/states_screens/dialogs/message_dialog.cpp:123 -#: src/states_screens/dialogs/message_dialog.cpp:136 +#: src/states_screens/dialogs/message_dialog.cpp:135 msgid "OK" msgstr "" @@ -825,7 +825,7 @@ msgstr "" #. I18N: ./data/gui/main_menu.stkgui #. I18N: In the main screen -#: src/states_screens/race_gui_overworld.cpp:462 +#: src/states_screens/race_gui_overworld.cpp:465 msgid "Tutorial" msgstr "" @@ -1317,7 +1317,7 @@ msgstr "" #. I18N: Section in the settings menu #: src/states_screens/options_screen_device.cpp:86 #: src/states_screens/options_screen_input.cpp:140 -#: src/states_screens/options_screen_ui.cpp:119 +#: src/states_screens/options_screen_ui.cpp:122 #: src/states_screens/options_screen_video.cpp:172 #: src/states_screens/user_screen.cpp:660 msgid "Audio" @@ -1351,7 +1351,7 @@ msgstr "" #. I18N: ./data/gui/options_input.stkgui #. I18N: Section in the settings menu #: src/states_screens/options_screen_audio.cpp:68 -#: src/states_screens/options_screen_ui.cpp:121 +#: src/states_screens/options_screen_ui.cpp:124 #: src/states_screens/options_screen_video.cpp:175 #: src/states_screens/user_screen.cpp:662 msgid "Controls" @@ -1393,7 +1393,7 @@ msgstr "" #: src/states_screens/options_screen_audio.cpp:67 #: src/states_screens/options_screen_device.cpp:88 #: src/states_screens/options_screen_input.cpp:142 -#: src/states_screens/options_screen_ui.cpp:120 +#: src/states_screens/options_screen_ui.cpp:123 #: src/states_screens/options_screen_video.cpp:174 #: src/states_screens/server_selection.cpp:103 msgid "Players" @@ -1405,7 +1405,7 @@ msgstr "" #. I18N: ./data/gui/options_players.stkgui #. I18N: In the player configuration screen -msgid "Press enter or double-click on a player to edit him/her" +msgid "Press enter or double-click on a player to edit their settings" msgstr "" #. I18N: ./data/gui/options_players.stkgui @@ -1458,7 +1458,7 @@ msgstr "" #: src/states_screens/options_screen_audio.cpp:65 #: src/states_screens/options_screen_device.cpp:85 #: src/states_screens/options_screen_input.cpp:139 -#: src/states_screens/options_screen_ui.cpp:118 +#: src/states_screens/options_screen_ui.cpp:121 #: src/states_screens/user_screen.cpp:659 msgid "Graphics" msgstr "" @@ -1871,46 +1871,47 @@ msgstr "" msgid "Completed achievement \"%s\"." msgstr "" -#: src/addons/addons_manager.cpp:97 src/addons/news_manager.cpp:317 +#: src/addons/addons_manager.cpp:104 src/addons/news_manager.cpp:325 msgid "Can't access stkaddons server..." msgstr "" -#: src/addons/news_manager.cpp:174 +#: src/addons/news_manager.cpp:182 #, c-format msgid "Error downloading news: '%s'." msgstr "" #. I18N: number of laps to race in a challenge -#: src/challenges/challenge_data.cpp:259 +#: src/challenges/challenge_data.cpp:266 +#: src/states_screens/race_result_gui.cpp:1422 #, c-format -msgid "Laps : %i" +msgid "Laps: %i" msgstr "" -#: src/challenges/challenge_data.cpp:265 +#: src/challenges/challenge_data.cpp:272 msgid "Follow the leader" msgstr "" -#: src/challenges/challenge_data.cpp:511 +#: src/challenges/challenge_data.cpp:518 #, c-format msgid "New track '%s' now available" msgstr "" -#: src/challenges/challenge_data.cpp:516 +#: src/challenges/challenge_data.cpp:523 #, c-format msgid "New game mode '%s' now available" msgstr "" -#: src/challenges/challenge_data.cpp:526 +#: src/challenges/challenge_data.cpp:533 #, c-format msgid "New Grand Prix '%s' now available" msgstr "" -#: src/challenges/challenge_data.cpp:530 +#: src/challenges/challenge_data.cpp:537 #, c-format msgid "New difficulty '%s' now available" msgstr "" -#: src/challenges/challenge_data.cpp:540 +#: src/challenges/challenge_data.cpp:547 #, c-format msgid "New kart '%s' now available" msgstr "" @@ -1942,17 +1943,17 @@ msgid "" "created." msgstr "" -#: src/graphics/irr_driver.cpp:1896 +#: src/graphics/irr_driver.cpp:1950 #, c-format msgid "FPS: %d/%d/%d - PolyCount: %d Solid, %d Shadows - LightDist : %d" msgstr "" -#: src/graphics/irr_driver.cpp:1907 +#: src/graphics/irr_driver.cpp:1961 #, c-format msgid "FPS: %d/%d/%d - %d KTris" msgstr "" -#: src/guiengine/engine.cpp:1346 +#: src/guiengine/engine.cpp:1289 msgid "Loading" msgstr "" @@ -2467,11 +2468,11 @@ msgid "Mouse axis %d %s" msgstr "" #. I18N: shown when config file is too old -#: src/input/device_manager.cpp:500 +#: src/input/device_manager.cpp:501 msgid "Please re-configure your key bindings." msgstr "" -#: src/input/device_manager.cpp:501 +#: src/input/device_manager.cpp:502 msgid "Your input config file is not compatible with this version of STK." msgstr "" @@ -2586,25 +2587,25 @@ msgstr "" msgid "Left thumb up" msgstr "" -#: src/input/input_manager.cpp:776 +#: src/input/input_manager.cpp:768 #, c-format msgid "Ignoring '%s'. You needed to join earlier to play!" msgstr "" -#: src/input/input_manager.cpp:806 +#: src/input/input_manager.cpp:798 msgid "Only the Game Master may act at this point!" msgstr "" #: src/input/wiimote_manager.cpp:389 msgid "" -"Connect your wiimote to the Bluetooth manager, then click on Ok.Detailed " +"Connect your wiimote to the Bluetooth manager, then click on Ok. Detailed " "instructions at supertuxkart.net/Wiimote" msgstr "" #: src/input/wiimote_manager.cpp:392 msgid "" "Press the buttons 1+2 simultaneously on your wiimote to put it in discovery " -"mode, then click on Ok.Detailed instructions at supertuxkart.net/Wiimote" +"mode, then click on Ok. Detailed instructions at supertuxkart.net/Wiimote" msgstr "" #: src/input/wiimote_manager.cpp:415 @@ -2618,23 +2619,23 @@ msgstr[1] "" msgid "Could not detect any wiimote :/" msgstr "" -#: src/karts/controller/local_player_controller.cpp:204 +#: src/karts/controller/local_player_controller.cpp:206 msgid "Penalty time!!" msgstr "" -#: src/karts/controller/local_player_controller.cpp:206 +#: src/karts/controller/local_player_controller.cpp:208 msgid "Don't accelerate before go" msgstr "" -#: src/karts/kart.cpp:867 src/karts/kart.cpp:872 +#: src/karts/kart.cpp:852 src/karts/kart.cpp:857 msgid "You won the race!" msgstr "" -#: src/karts/kart.cpp:872 +#: src/karts/kart.cpp:857 msgid "You finished the race!" msgstr "" -#: src/main.cpp:1296 +#: src/main.cpp:1413 msgid "" "SuperTuxKart may connect to a server to download add-ons and notify you of " "updates. We also collect anonymous hardware statistics to help with the " @@ -2644,15 +2645,16 @@ msgid "" "edit \"Connect to the Internet\" and \"Send anonymous HW statistics\")." msgstr "" -#: src/main.cpp:1446 +#: src/main.cpp:1563 msgid "" "Your driver version is too old. Please install the latest video drivers." msgstr "" -#: src/main.cpp:1458 +#: src/main.cpp:1580 +#, c-format msgid "" "Your OpenGL version appears to be too old. Please verify if an update for " -"your video driver is available. SuperTuxKart requires OpenGL 3.1 or better." +"your video driver is available. SuperTuxKart requires %s or better." msgstr "" #: src/modes/easter_egg_hunt.cpp:202 @@ -2660,7 +2662,7 @@ msgstr "" msgid "Eggs: %d / %d" msgstr "" -#: src/modes/follow_the_leader.cpp:61 src/modes/follow_the_leader.cpp:293 +#: src/modes/follow_the_leader.cpp:61 src/modes/follow_the_leader.cpp:284 msgid "Leader" msgstr "" @@ -2687,11 +2689,11 @@ msgstr "" msgid "WRONG WAY!" msgstr "" -#: src/modes/world.cpp:1208 +#: src/modes/world.cpp:1207 msgid "You have been eliminated!" msgstr "" -#: src/modes/world.cpp:1211 +#: src/modes/world.cpp:1210 #, c-format msgid "'%s' has been eliminated." msgstr "" @@ -2700,7 +2702,7 @@ msgstr "" msgid "Failed to register server" msgstr "" -#: src/network/servers_manager.cpp:195 +#: src/network/servers_manager.cpp:197 msgid "No LAN server detected" msgstr "" @@ -2815,26 +2817,32 @@ msgstr "" msgid "2 years" msgstr "" -#: src/states_screens/addons_screen.cpp:110 +#: src/states_screens/addons_screen.cpp:115 msgid "Add-on name" msgstr "" -#: src/states_screens/addons_screen.cpp:111 +#: src/states_screens/addons_screen.cpp:116 msgid "Updated date" msgstr "" +#: src/states_screens/addons_screen.cpp:147 +msgid "" +"Access to the Internet is disabled. (To enable it, go to options and select " +"tab 'User Interface')" +msgstr "" + #. I18N: as in: The Old Island by Johannes Sjolund -#: src/states_screens/addons_screen.cpp:304 +#: src/states_screens/addons_screen.cpp:343 #, c-format msgctxt "addons" msgid "%s by %s" msgstr "" -#: src/states_screens/addons_screen.cpp:435 +#: src/states_screens/addons_screen.cpp:474 msgid "Please wait while addons are updated" msgstr "" -#: src/states_screens/addons_screen.cpp:512 +#: src/states_screens/addons_screen.cpp:551 #: src/states_screens/main_menu_screen.cpp:553 msgid "" "Sorry, an error occurred while contacting the add-ons website. Make sure you " @@ -3210,32 +3218,32 @@ msgstr "" msgid "Select a track" msgstr "" -#: src/states_screens/feature_unlocked.cpp:199 +#: src/states_screens/feature_unlocked.cpp:200 #, c-format msgid "You completed the easy challenge! Points earned on this level: %i/%i" msgstr "" -#: src/states_screens/feature_unlocked.cpp:203 +#: src/states_screens/feature_unlocked.cpp:204 #, c-format msgid "" "You completed the intermediate challenge! Points earned on this level: %i/%i" msgstr "" -#: src/states_screens/feature_unlocked.cpp:207 +#: src/states_screens/feature_unlocked.cpp:208 #, c-format msgid "" "You completed the difficult challenge! Points earned on this level: %i/%i" msgstr "" -#: src/states_screens/feature_unlocked.cpp:492 +#: src/states_screens/feature_unlocked.cpp:493 msgid "Challenge Completed" msgstr "" -#: src/states_screens/feature_unlocked.cpp:529 +#: src/states_screens/feature_unlocked.cpp:530 msgid "You unlocked track %0" msgstr "" -#: src/states_screens/feature_unlocked.cpp:567 +#: src/states_screens/feature_unlocked.cpp:568 msgid "You unlocked grand prix %0" msgstr "" @@ -3271,16 +3279,16 @@ msgid "User defined" msgstr "" #. I18N: when failing a GP -#: src/states_screens/grand_prix_lose.cpp:153 +#: src/states_screens/grand_prix_lose.cpp:154 msgid "Better luck next time!" msgstr "" -#: src/states_screens/grand_prix_win.cpp:126 +#: src/states_screens/grand_prix_win.cpp:127 #: src/states_screens/race_result_gui.cpp:194 msgid "You completed a challenge!" msgstr "" -#: src/states_screens/grand_prix_win.cpp:283 +#: src/states_screens/grand_prix_win.cpp:284 msgid "You completed the Grand Prix!" msgstr "" @@ -3505,14 +3513,14 @@ msgstr "" msgid "Keyboard %i" msgstr "" -#: src/states_screens/options_screen_ui.cpp:155 +#: src/states_screens/options_screen_ui.cpp:158 msgid "" "In multiplayer mode, players can select handicapped (more difficult) " "profiles on the kart selection screen" msgstr "" #. I18N: in the language choice, to select the same language as the OS -#: src/states_screens/options_screen_ui.cpp:187 +#: src/states_screens/options_screen_ui.cpp:190 msgid "System Language" msgstr "" @@ -3642,20 +3650,20 @@ msgid "GOAL!" msgstr "" #. I18N: string used to show the author of the music. (e.g. "Sunny Song" by "John Doe") -#: src/states_screens/race_gui_base.cpp:504 +#: src/states_screens/race_gui_base.cpp:496 msgid "by" msgstr "" -#: src/states_screens/race_gui_base.cpp:612 +#: src/states_screens/race_gui_base.cpp:604 msgid "Collect nitro!" msgstr "" -#: src/states_screens/race_gui_base.cpp:614 +#: src/states_screens/race_gui_base.cpp:606 msgid "Follow the leader!" msgstr "" #. I18N: When some GlobalPlayerIcons are hidden, write "Top 10" to show it -#: src/states_screens/race_gui_base.cpp:780 +#: src/states_screens/race_gui_base.cpp:772 #, c-format msgid "Top %i" msgstr "" @@ -3669,15 +3677,15 @@ msgstr "" msgid "Rank" msgstr "" -#: src/states_screens/race_gui_overworld.cpp:469 +#: src/states_screens/race_gui_overworld.cpp:472 msgid "Press fire to play the tutorial" msgstr "" -#: src/states_screens/race_gui_overworld.cpp:508 +#: src/states_screens/race_gui_overworld.cpp:511 msgid "Type: Grand Prix" msgstr "" -#: src/states_screens/race_gui_overworld.cpp:539 +#: src/states_screens/race_gui_overworld.cpp:548 msgid "Press fire to start the challenge" msgstr "" @@ -3745,10 +3753,20 @@ msgstr "" msgid "Grand Prix progress:" msgstr "" -#: src/states_screens/race_result_gui.cpp:1341 +#: src/states_screens/race_result_gui.cpp:1344 msgid "Highscores" msgstr "" +#: src/states_screens/race_result_gui.cpp:1430 +#, c-format +msgid "Difficulty: %s" +msgstr "" + +#: src/states_screens/race_result_gui.cpp:1438 +#, c-format +msgid "Best lap time: %s" +msgstr "" + #: src/states_screens/race_setup_screen.cpp:87 msgid "All blows allowed, so catch weapons and make clever use of them!" msgstr "" @@ -3767,7 +3785,7 @@ msgid "Hit others with weapons until they lose all their lives." msgstr "" #: src/states_screens/race_setup_screen.cpp:119 -msgid "Push the ball to the opposite cage to score goals." +msgid "Push the ball into the opposite cage to score goals." msgstr "" #: src/states_screens/race_setup_screen.cpp:129 From 061323ffb02d6684579f842747b47356dd509819 Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Tue, 23 Aug 2016 20:01:06 -0400 Subject: [PATCH 141/350] Add information for translators --- data/po/supertuxkart.pot | 15 ++++++++------- src/states_screens/main_menu_screen.cpp | 1 + 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/data/po/supertuxkart.pot b/data/po/supertuxkart.pot index d7f7c3f7b..08bddec9a 100644 --- a/data/po/supertuxkart.pot +++ b/data/po/supertuxkart.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: supertuxkart\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-08-23 19:52-0400\n" +"POT-Creation-Date: 2016-08-23 20:00-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -996,7 +996,8 @@ msgstr "" #. I18N: ./data/gui/online/guest_login.stkgui #. I18N: ./data/gui/user_screen.stkgui -#: src/states_screens/main_menu_screen.cpp:79 +#. I18N: Used as a verb, appears on the main menu (login button) +#: src/states_screens/main_menu_screen.cpp:80 msgid "Login" msgstr "" @@ -2843,7 +2844,7 @@ msgid "Please wait while addons are updated" msgstr "" #: src/states_screens/addons_screen.cpp:551 -#: src/states_screens/main_menu_screen.cpp:553 +#: src/states_screens/main_menu_screen.cpp:554 msgid "" "Sorry, an error occurred while contacting the add-ons website. Make sure you " "are connected to the Internet and that SuperTuxKart is not blocked by a " @@ -3307,25 +3308,25 @@ msgid "" "Press the 'Select' button to join the game" msgstr "" -#: src/states_screens/main_menu_screen.cpp:492 +#: src/states_screens/main_menu_screen.cpp:493 msgid "" "You can not play online without internet access. If you want to play online, " "go to options, select tab 'User Interface', and edit \"Connect to the " "Internet\"." msgstr "" -#: src/states_screens/main_menu_screen.cpp:516 +#: src/states_screens/main_menu_screen.cpp:517 msgid "" "You can not download addons without internet access. If you want to download " "addons, go to options, select tab 'User Interface', and edit \"Connect to " "the Internet\"." msgstr "" -#: src/states_screens/main_menu_screen.cpp:548 +#: src/states_screens/main_menu_screen.cpp:549 msgid "The add-ons module is currently disabled in the Options screen" msgstr "" -#: src/states_screens/main_menu_screen.cpp:560 +#: src/states_screens/main_menu_screen.cpp:561 msgid "Please wait while the add-ons are loading" msgstr "" diff --git a/src/states_screens/main_menu_screen.cpp b/src/states_screens/main_menu_screen.cpp index c3c2da955..54ce7efa3 100644 --- a/src/states_screens/main_menu_screen.cpp +++ b/src/states_screens/main_menu_screen.cpp @@ -76,6 +76,7 @@ bool MainMenuScreen::m_enable_online = false; MainMenuScreen::MainMenuScreen() : Screen("main_menu.stkgui") { m_online_string = _("Online"); + //I18N: Used as a verb, appears on the main menu (login button) m_login_string = _("Login"); } // MainMenuScreen From 5abc047b0c8e8ce9abf8e271970463f5b2e1c55e Mon Sep 17 00:00:00 2001 From: deve Date: Fri, 26 Aug 2016 10:35:48 +0200 Subject: [PATCH 142/350] Declare precision also in vertex shaders in GLES renderer. Some drivers complain that it was declared only in fragment shaders. --- data/shaders/pass_gles.vert | 2 ++ src/graphics/shader.cpp | 32 ++++++++++++++++++-------------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/data/shaders/pass_gles.vert b/data/shaders/pass_gles.vert index 822499f98..99edd86e1 100644 --- a/data/shaders/pass_gles.vert +++ b/data/shaders/pass_gles.vert @@ -1,5 +1,7 @@ #version 300 es +precision mediump float; + in vec3 Position; in vec2 Texcoord; out vec2 uv; diff --git a/src/graphics/shader.cpp b/src/graphics/shader.cpp index 1b5e4bf0b..306561d15 100644 --- a/src/graphics/shader.cpp +++ b/src/graphics/shader.cpp @@ -62,7 +62,7 @@ const std::string& ShaderBase::getHeader() GLuint ShaderBase::loadShader(const std::string &file, unsigned type) { GLuint id = glCreateShader(type); - + std::ostringstream code; #if !defined(USE_GLES2) code << "#version " << CVS->getGLSLVersion()<<"\n"; @@ -87,13 +87,13 @@ GLuint ShaderBase::loadShader(const std::string &file, unsigned type) code << "#extension GL_ARB_arrays_of_arrays : enable\n"; } #endif - + if (CVS->isAMDVertexShaderLayerUsable()) code << "#extension GL_AMD_vertex_shader_layer : enable\n"; - + if (CVS->isARBExplicitAttribLocationUsable()) code << "#extension GL_ARB_explicit_attrib_location : enable\n"; - + if (CVS->isAZDOEnabled()) { code << "#extension GL_ARB_bindless_texture : enable\n"; @@ -110,7 +110,11 @@ GLuint ShaderBase::loadShader(const std::string &file, unsigned type) //shader compilation fails with some drivers if there is no precision qualifier if (type == GL_FRAGMENT_SHADER) code << "precision mediump float;\n"; - +#if defined(USE_GLES2) + else if (type == GL_VERTEX_SHADER) + code << "precision mediump float;\n"; +#endif + code << getHeader(); std::ifstream stream(file_manager->getShader(file), std::ios::in); @@ -129,31 +133,31 @@ GLuint ShaderBase::loadShader(const std::string &file, unsigned type) Log::error("shader", "Invalid #stk_include line: '%s'.", Line.c_str()); continue; } - + std::string filename = Line.substr(pos+1); - + pos = filename.find("\""); if (pos == std::string::npos) { Log::error("shader", "Invalid #stk_include line: '%s'.", Line.c_str()); continue; } - + filename = filename.substr(0, pos); - + std::ifstream include_stream(file_manager->getShader(filename), std::ios::in); if (!include_stream.is_open()) { Log::error("shader", "Couldn't open included shader: '%s'.", filename.c_str()); continue; } - + std::string include_line = ""; while (getline(include_stream, include_line)) { code << "\n" << include_line; } - + include_stream.close(); } else @@ -161,7 +165,7 @@ GLuint ShaderBase::loadShader(const std::string &file, unsigned type) code << "\n" << Line; } } - + stream.close(); } else @@ -201,7 +205,7 @@ GLuint ShaderBase::loadShader(const std::string &file, unsigned type) /** Loads a transform feedback buffer shader with a given number of varying * parameters. */ -int ShaderBase::loadTFBProgram(const std::string &shader_name, +int ShaderBase::loadTFBProgram(const std::string &shader_name, const char **varyings, unsigned varying_count) { @@ -250,7 +254,7 @@ void ShaderBase::bypassUBO() const glUniformMatrix4fv(IPM, 1, GL_FALSE, irr_driver->getInvProjMatrix().pointer()); GLint Screen = glGetUniformLocation(m_program, "screen"); - glUniform2f(Screen, irr_driver->getCurrentScreenSize().X, + glUniform2f(Screen, irr_driver->getCurrentScreenSize().X, irr_driver->getCurrentScreenSize().Y); GLint bLmn = glGetUniformLocation(m_program, "blueLmn[0]"); From ce91d6c2b4843cfb5dcfd540c69095d56ac1f3e0 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 31 Aug 2016 16:27:05 +1000 Subject: [PATCH 143/350] Bugfix (saved incorrect variable). --- src/karts/controller/kart_control.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/karts/controller/kart_control.cpp b/src/karts/controller/kart_control.cpp index accebac1f..5532949c1 100644 --- a/src/karts/controller/kart_control.cpp +++ b/src/karts/controller/kart_control.cpp @@ -66,7 +66,7 @@ void KartControl::setSteer(float f) /** Sets the acceleration. */ void KartControl::setAccel(float f) { - float old_accel = m_steer; + float old_accel = m_accel; m_accel = f; RewindManager *re = RewindManager::get(); if (re->isEnabled() && !re->isRewinding() && old_accel != m_accel) From 2a06036fb7c80bf39ce0f4ce032a8147add6dea6 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 31 Aug 2016 16:31:59 +1000 Subject: [PATCH 144/350] Changed order in which rendering and various updates are done, which results in the physics reacting one frame earlier to user input. --- src/karts/kart.cpp | 25 ++++++++++---------- src/main_loop.cpp | 13 ++++++---- src/modes/world.cpp | 43 +++++++++++++++++++++++----------- src/modes/world.hpp | 5 ++-- src/modes/world_status.cpp | 15 ++++++++++-- src/modes/world_status.hpp | 7 +++--- src/network/rewind_manager.cpp | 1 + 7 files changed, 72 insertions(+), 37 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 746f0e1f0..2735b0909 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1174,18 +1174,6 @@ void Kart::eliminate() */ void Kart::update(float dt) { -#ifdef DEBUG_TO_COMPARE_KART_PHYSICS - // This information is useful when comparing kart physics, e.g. to - // see top speed, acceleration (i.e. time to top speed) etc. - Log::verbose("physics", "%s t %f %f xyz %f %f %f v %f %f %f s %f a %f", - getIdent().c_str(), - World::getWorld()->getTime(), dt, - getXYZ().getX(), getXYZ().getY(), getXYZ().getZ(), - getVelocity().getX(), getVelocity().getY(), getVelocity().getZ(), - getControls().getSteer(), - getControls().getAccel()); -#endif - // update star effect (call will do nothing if stars are not activated) m_stars_effect->update(dt); @@ -1223,6 +1211,19 @@ void Kart::update(float dt) if(!history->replayHistory() && !RewindManager::get()->isRewinding()) m_controller->update(dt); +#undef DEBUG_TO_COMPARE_KART_PHYSICS +#ifdef DEBUG_TO_COMPARE_KART_PHYSICS + // This information is useful when comparing kart physics, e.g. to + // see top speed, acceleration (i.e. time to top speed) etc. + Log::verbose("physics", "%s t %f %f xyz %f %f %f v %f %f %f s %f a %f", + getIdent().c_str(), + World::getWorld()->getTime(), dt, + getXYZ().getX(), getXYZ().getY(), getXYZ().getZ(), + getVelocity().getX(), getVelocity().getY(), getVelocity().getZ(), + getControls().getSteer(), + getControls().getAccel()); +#endif + // if its view is blocked by plunger, decrease remaining time if(m_view_blocked_by_plunger > 0) m_view_blocked_by_plunger -= dt; //unblock the view if kart just became shielded diff --git a/src/main_loop.cpp b/src/main_loop.cpp index 42ef03ab7..13b57950b 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -213,6 +213,11 @@ void MainLoop::run() m_prev_time = m_curr_time; float dt = getLimitedDt(); + // Render the previous frame, and also handle all user input. + PROFILER_PUSH_CPU_MARKER("IrrDriver update", 0x00, 0x00, 0x7F); + irr_driver->update(dt); + PROFILER_POP_CPU_MARKER(); + if (World::getWorld()) // race is active if world exists { @@ -238,10 +243,6 @@ void MainLoop::run() GUIEngine::update(dt); PROFILER_POP_CPU_MARKER(); - PROFILER_PUSH_CPU_MARKER("IrrDriver update", 0x00, 0x00, 0x7F); - irr_driver->update(dt); - PROFILER_POP_CPU_MARKER(); - // Update sfx and music after graphics, so that graphics code // can use as many threads as possible without interfering // with audia @@ -275,6 +276,10 @@ void MainLoop::run() PROFILER_POP_CPU_MARKER(); } + // Update world time if world exists + if (World::getWorld()) + World::getWorld()->updateTime(dt); + PROFILER_POP_CPU_MARKER(); PROFILER_SYNC_FRAME(); } // while !m_abort diff --git a/src/modes/world.cpp b/src/modes/world.cpp index a9c91fea7..f36ca0685 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -974,7 +974,7 @@ void World::scheduleTutorial() { m_schedule_exit_race = true; m_schedule_tutorial = true; -} +} // scheduleTutorial //----------------------------------------------------------------------------- /** Updates the physics, all karts, the track, and projectile manager. @@ -999,22 +999,14 @@ void World::update(float dt) } #endif - PROFILER_PUSH_CPU_MARKER("World::update (sub-updates)", 0x20, 0x7F, 0x00); - history->update(dt); - if(race_manager->isRecordingRace()) ReplayRecorder::get()->update(dt); - if(history->replayHistory()) dt=history->getNextDelta(); - RewindManager::get()->setCurrentTime(World::getWorld()->getTime(), dt); - RewindManager::get()->saveStates(); WorldStatus::update(dt); - if (m_script_engine) m_script_engine->update(dt); - PROFILER_POP_CPU_MARKER(); - - if (!history->dontDoPhysics()) - { - m_physics->update(dt); - } + RewindManager::get()->saveStates(); PROFILER_PUSH_CPU_MARKER("World::update (Kart::upate)", 0x40, 0x7F, 0x00); + + // Update all the karts. This in turn will also update the controller, + // which causes all AI steering commands set. So in the following + // physics update the new steering is taken into account. const int kart_amount = (int)m_karts.size(); for (int i = 0 ; i < kart_amount; ++i) { @@ -1030,6 +1022,18 @@ void World::update(float dt) } PROFILER_POP_CPU_MARKER(); + PROFILER_PUSH_CPU_MARKER("World::update (sub-updates)", 0x20, 0x7F, 0x00); + history->update(dt); + if(race_manager->isRecordingRace()) ReplayRecorder::get()->update(dt); + if(history->replayHistory()) dt=history->getNextDelta(); + if (m_script_engine) m_script_engine->update(dt); + PROFILER_POP_CPU_MARKER(); + + if (!history->dontDoPhysics()) + { + m_physics->update(dt); + } + PROFILER_PUSH_CPU_MARKER("World::update (weather)", 0x80, 0x7F, 0x00); if (UserConfigParams::m_graphical_effects && m_weather) { @@ -1048,6 +1052,17 @@ void World::update(float dt) #endif } // update +// ---------------------------------------------------------------------------- +/** Compute the new time, and set this new time to be used in the rewind + * manager. + * \param dt Time step size. + */ +void World::updateTime(const float dt) +{ + WorldStatus::updateTime(dt); + RewindManager::get()->setCurrentTime(getTime(), dt); +} // updateTime + // ---------------------------------------------------------------------------- /** Only updates the track. The order in which the various parts of STK are * updated is quite important (i.e. the track can't be updated as part of diff --git a/src/modes/world.hpp b/src/modes/world.hpp index 3a03d0bf2..aeca3c166 100644 --- a/src/modes/world.hpp +++ b/src/modes/world.hpp @@ -178,7 +178,7 @@ protected: virtual void onGo() OVERRIDE; /** Returns true if the race is over. Must be defined by all modes. */ virtual bool isRaceOver() = 0; - virtual void update(float dt); + virtual void update(float dt) OVERRIDE; virtual void createRaceGUI(); void updateTrack(float dt); // ------------------------------------------------------------------------ @@ -252,9 +252,10 @@ public: // ================= virtual void init(); virtual void terminateRace() OVERRIDE; - virtual void reset(); + virtual void reset() OVERRIDE; virtual void pause(Phase phase) OVERRIDE; virtual void unpause() OVERRIDE; + virtual void updateTime(const float dt) OVERRIDE; virtual void getDefaultCollectibles(int *collectible_type, int *amount ); virtual void endRaceEarly() { return; } diff --git a/src/modes/world_status.cpp b/src/modes/world_status.cpp index 81e066a0e..c863caa0c 100644 --- a/src/modes/world_status.cpp +++ b/src/modes/world_status.cpp @@ -153,10 +153,21 @@ void WorldStatus::terminateRace() } // terminateRace //----------------------------------------------------------------------------- -/** Updates all status information, called once per frame. +/** Update, called once per frame. Called early on before physics are + * updated. + * \param dt Time step. + */ +void WorldStatus::update(float dt) +{ +} // update + +//----------------------------------------------------------------------------- +/** Updates the world time and clock (which might be running backwards), and + * all status information, called once per frame at the end of the main + * loop. * \param dt Duration of time step. */ -void WorldStatus::update(const float dt) +void WorldStatus::updateTime(const float dt) { switch (m_phase) { diff --git a/src/modes/world_status.hpp b/src/modes/world_status.hpp index 0a329d8f9..3e2e5fe3c 100644 --- a/src/modes/world_status.hpp +++ b/src/modes/world_status.hpp @@ -122,13 +122,14 @@ public: WorldStatus(); virtual ~WorldStatus(); - void reset(); - void update(const float dt); - void setTime(const float time); + virtual void reset(); + virtual void updateTime(const float dt); + virtual void update(float dt); virtual void pause(Phase phase); virtual void unpause(); virtual void enterRaceOverState(); virtual void terminateRace(); + void setTime(const float time); // ------------------------------------------------------------------------ // Note: GO_PHASE is both: start phase and race phase diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index 7f06946eb..8ff236e01 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -363,6 +363,7 @@ void RewindManager::rewindTo(float rewind_time) #ifdef SHOW_ROLLBACK irr_driver->update(dt); #endif + world->updateTime(dt); } m_is_rewinding = false; From 3dc13eb071c5f96de34ac64c37ebf59c8f3b2f7b Mon Sep 17 00:00:00 2001 From: LoadingPleaseWait Date: Wed, 31 Aug 2016 15:36:15 -0500 Subject: [PATCH 145/350] Fix CPU marker typo --- src/modes/world.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 10c96b606..86394060e 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -1006,7 +1006,7 @@ void World::update(float dt) m_physics->update(dt); } - PROFILER_PUSH_CPU_MARKER("World::update (Kart::upate)", 0x40, 0x7F, 0x00); + PROFILER_PUSH_CPU_MARKER("World::update (Kart::update)", 0x40, 0x7F, 0x00); const int kart_amount = (int)m_karts.size(); for (int i = 0 ; i < kart_amount; ++i) { From f89aaa67f5272a87f566ffc4dbe7aab6d4639d11 Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 1 Sep 2016 09:54:09 +1000 Subject: [PATCH 146/350] Reduce (admittedly very minor) camera jitter by computing the speed of a kart after it was capped by max speed. --- src/karts/kart.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 2735b0909..85d2f6cb5 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -2174,6 +2174,13 @@ void Kart::updatePhysics(float dt) updateSliding(); + // Cap speed if necessary + const Material *m = getMaterial(); + + float min_speed = m && m->isZipper() ? m->getZipperMinSpeed() : -1.0f; + m_max_speed->setMinSpeed(min_speed); + m_max_speed->update(dt); + // Compute the speed of the kart. m_speed = getVehicle()->getRigidBody()->getLinearVelocity().length(); @@ -2187,13 +2194,6 @@ void Kart::updatePhysics(float dt) if (forwardW.dot(getVehicle()->getRigidBody()->getLinearVelocity()) < btScalar(0.)) m_speed *= -1.f; - // Cap speed if necessary - const Material *m = getMaterial(); - - float min_speed = m && m->isZipper() ? m->getZipperMinSpeed() : -1.0f; - m_max_speed->setMinSpeed(min_speed); - m_max_speed->update(dt); - // To avoid tunneling (which can happen on long falls), clamp the // velocity in Y direction. Tunneling can happen if the Y velocity // is larger than the maximum suspension travel (per frame), since then From b91ecc9754f643cbb39fe9f3768e3a168470a4d0 Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 1 Sep 2016 15:52:26 +1000 Subject: [PATCH 147/350] Use exponential smoothing for speed, to further reduce stuttering in speed (and therefore camera). --- src/karts/kart.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 85d2f6cb5..37be5f118 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -2181,8 +2181,16 @@ void Kart::updatePhysics(float dt) m_max_speed->setMinSpeed(min_speed); m_max_speed->update(dt); - // Compute the speed of the kart. + // Compute the speed of the kart. Smooth it with previous speed to make + // the camera smoother (because of capping the speed in m_max_speed + // the speed value jitters when approaching maximum speed. This results + // in the distance between kart and camera to jitter as well (typically + // only in the order of centimetres though). Smoothing the speed value + // gets rid of this jitter, and also r + float old_speed = m_speed; m_speed = getVehicle()->getRigidBody()->getLinearVelocity().length(); + float f=1.0f; + m_speed = f*m_speed + (1.0f-f)*old_speed; // calculate direction of m_speed const btTransform& chassisTrans = getVehicle()->getChassisWorldTransform(); From 78baaff23e51b194e33cbed38b29605186335b34 Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 3 Sep 2016 23:03:14 +0000 Subject: [PATCH 148/350] Fix crash in profile mode --- src/graphics/irr_driver.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 1bd49ab04..048f7b88c 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -535,7 +535,8 @@ void IrrDriver::initDevice() // pipeline doesn't work for them. For example some radeon drivers // support only GLSL 1.3 and it causes STK to crash. We should force to use // fixed pipeline in this case. - if (GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_FORCE_LEGACY_DEVICE)) + if (!ProfileWorld::isNoGraphics() && + GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_FORCE_LEGACY_DEVICE)) { Log::warn("irr_driver", "Driver doesn't support shader-based pipeline. " "Re-creating device to workaround the issue."); From 21dc569f703e6e26c7e6df31c83205f0f1a103c6 Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 4 Sep 2016 11:56:03 +0800 Subject: [PATCH 149/350] Start to clean up quad and graph node --- sources.cmake | 2 +- src/graphics/slip_stream.cpp | 2 +- src/items/bowling.cpp | 3 +- src/items/item.cpp | 2 + src/items/item_manager.cpp | 7 +- src/items/rubber_ball.cpp | 9 +- .../controller/ai_base_lap_controller.cpp | 2 +- src/karts/controller/end_controller.cpp | 7 +- src/karts/controller/skidding_ai.cpp | 172 +++++------- src/karts/controller/skidding_ai.hpp | 10 +- src/karts/controller/test_ai.cpp | 213 +++++++++------ src/karts/controller/test_ai.hpp | 7 +- src/karts/kart.cpp | 8 +- src/modes/linear_world.cpp | 7 +- src/physics/physical_object.cpp | 1 + src/scriptengine/script_engine.hpp | 2 + src/tracks/battle_graph.cpp | 15 +- src/tracks/battle_graph.hpp | 2 +- src/tracks/check_manager.cpp | 3 +- src/tracks/graph_node.cpp | 254 +++--------------- src/tracks/graph_node.hpp | 171 +++++------- src/tracks/navmesh.cpp | 8 + src/tracks/node_2d.cpp | 69 +++++ src/tracks/node_2d.hpp | 55 ++++ src/tracks/node_3d.cpp | 94 +++++++ src/tracks/node_3d.hpp | 64 +++++ src/tracks/quad.cpp | 91 +------ src/tracks/quad.hpp | 55 ++-- src/tracks/quad_graph.cpp | 244 +++++++++++------ src/tracks/quad_graph.hpp | 69 ++--- src/tracks/quad_set.cpp | 124 --------- src/tracks/quad_set.hpp | 90 ------- src/tracks/track.cpp | 13 +- src/tracks/track.hpp | 16 +- src/tracks/track_sector.cpp | 4 +- 35 files changed, 867 insertions(+), 1028 deletions(-) create mode 100644 src/tracks/node_2d.cpp create mode 100644 src/tracks/node_2d.hpp create mode 100644 src/tracks/node_3d.cpp create mode 100644 src/tracks/node_3d.hpp delete mode 100644 src/tracks/quad_set.cpp delete mode 100644 src/tracks/quad_set.hpp diff --git a/sources.cmake b/sources.cmake index f484b15d5..ddc029d4f 100644 --- a/sources.cmake +++ b/sources.cmake @@ -1,5 +1,5 @@ # Modify this file to change the last-modified date when you add/remove a file. -# This will then trigger a new cmake run automatically. +# This will then trigger a new cmake run automatically. file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp") file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp") file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*") diff --git a/src/graphics/slip_stream.cpp b/src/graphics/slip_stream.cpp index aebb95134..dea3781c6 100644 --- a/src/graphics/slip_stream.cpp +++ b/src/graphics/slip_stream.cpp @@ -457,7 +457,7 @@ void SlipStream::update(float dt) } // Real test: if in slipstream quad of other kart if(m_target_kart->getSlipstream()->m_slipstream_quad - ->pointInQuad(m_kart->getXYZ())) + ->pointInside(m_kart->getXYZ())) { is_sstreaming = true; break; diff --git a/src/items/bowling.cpp b/src/items/bowling.cpp index aa3070ae1..dd2971fcf 100644 --- a/src/items/bowling.cpp +++ b/src/items/bowling.cpp @@ -26,6 +26,7 @@ #include "karts/abstract_kart.hpp" #include "modes/linear_world.hpp" #include "utils/random_generator.hpp" +#include "tracks/graph_node.hpp" #include "tracks/quad_graph.hpp" #include "utils/log.hpp" //TODO: remove after debugging is done @@ -63,7 +64,7 @@ Bowling::Bowling(AbstractKart *kart) { unsigned int sector = ((LinearWorld*)World::getWorld())-> getTrackSector(kart->getWorldKartId()).getCurrentGraphNode(); - quadNormal = QuadGraph::get()->getQuadOfNode(sector).getNormal(); + quadNormal = QuadGraph::get()->getNode(sector).getNormal(); } else quadNormal = btVector3(.0f, 1.0f, .0f); diff --git a/src/items/item.cpp b/src/items/item.cpp index 8f5a0c07b..d1dd8ca0e 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -25,6 +25,8 @@ #include "modes/easter_egg_hunt.hpp" #include "modes/three_strikes_battle.hpp" #include "modes/world.hpp" +#include "tracks/graph_node.hpp" +#include "tracks/quad_graph.hpp" #include "tracks/track.hpp" #include "utils/constants.hpp" #include "utils/vec3.hpp" diff --git a/src/items/item_manager.cpp b/src/items/item_manager.cpp index 3141547f3..6d72e8d87 100644 --- a/src/items/item_manager.cpp +++ b/src/items/item_manager.cpp @@ -32,8 +32,9 @@ #include "modes/linear_world.hpp" #include "network/network_config.hpp" #include "network/race_event_manager.hpp" -#include "tracks/quad_graph.hpp" #include "tracks/battle_graph.hpp" +#include "tracks/graph_node.hpp" +#include "tracks/quad_graph.hpp" #include "tracks/track.hpp" #include "utils/string_utils.hpp" @@ -162,7 +163,7 @@ ItemManager::ItemManager() m_items_in_quads = new std::vector; // Entries 0 to n-1 are for the quads, entry // n is for all items that are not on a quad. - m_items_in_quads->resize(QuadSet::get()->getNumberOfQuads()+1); + m_items_in_quads->resize(QuadGraph::get()->getNumNodes()+1); } else { @@ -227,7 +228,7 @@ void ItemManager::insertItem(Item *item) // If the item is on the driveline, store it at the appropriate index if(graph_node > -1) { - int sector = QuadGraph::get()->getNode(graph_node).getQuadIndex(); + int sector = QuadGraph::get()->getNode(graph_node).getNodeIndex(); (*m_items_in_quads)[sector].push_back(item); } else // otherwise store it in the 'outside' index diff --git a/src/items/rubber_ball.cpp b/src/items/rubber_ball.cpp index c46611daf..961c2fbfe 100644 --- a/src/items/rubber_ball.cpp +++ b/src/items/rubber_ball.cpp @@ -29,6 +29,8 @@ #include "modes/linear_world.hpp" #include "physics/btKart.hpp" #include "physics/triangle_mesh.hpp" +#include "tracks/graph_node.hpp" +#include "tracks/quad_graph.hpp" #include "tracks/track.hpp" #include "utils/log.hpp" //TODO: remove after debugging is done @@ -97,7 +99,8 @@ RubberBall::RubberBall(AbstractKart *kart) // initialises the current graph node TrackSector::update(getXYZ()); - Vec3 normal = QuadGraph::get()->getQuadOfNode(getCurrentGraphNode()).getNormal(); + const Vec3& normal = + QuadGraph::get()->getNode(getCurrentGraphNode()).getNormal(); TerrainInfo::update(getXYZ(), -normal); initializeControlPoints(m_owner->getXYZ()); @@ -133,7 +136,7 @@ void RubberBall::initializeControlPoints(const Vec3 &xyz) // left or right when firing the ball off track. getNextControlPoint(); m_control_points[2] = - QuadGraph::get()->getQuadOfNode(m_last_aimed_graph_node).getCenter(); + QuadGraph::get()->getNode(m_last_aimed_graph_node).getCenter(); // This updates m_last_aimed_graph_node, and sets m_control_points[3] getNextControlPoint(); @@ -234,7 +237,7 @@ void RubberBall::getNextControlPoint() m_last_aimed_graph_node = next; m_length_cp_2_3 = dist; const Quad &quad = - QuadGraph::get()->getQuadOfNode(m_last_aimed_graph_node); + QuadGraph::get()->getNode(m_last_aimed_graph_node); m_control_points[3] = quad.getCenter(); } // getNextControlPoint diff --git a/src/karts/controller/ai_base_lap_controller.cpp b/src/karts/controller/ai_base_lap_controller.cpp index 899cfb5ed..23c5f0647 100644 --- a/src/karts/controller/ai_base_lap_controller.cpp +++ b/src/karts/controller/ai_base_lap_controller.cpp @@ -25,7 +25,7 @@ #include "karts/kart_properties.hpp" #include "karts/controller/ai_properties.hpp" #include "modes/linear_world.hpp" -#include "tracks/track.hpp" +#include "tracks/quad_graph.hpp" #include "utils/constants.hpp" diff --git a/src/karts/controller/end_controller.cpp b/src/karts/controller/end_controller.cpp index efe1f6134..5881c23a8 100644 --- a/src/karts/controller/end_controller.cpp +++ b/src/karts/controller/end_controller.cpp @@ -45,6 +45,7 @@ #include "modes/linear_world.hpp" #include "race/race_manager.hpp" #include "states_screens/race_result_gui.hpp" +#include "tracks/graph_node.hpp" #include "tracks/quad_graph.hpp" #include "tracks/track.hpp" #include "utils/constants.hpp" @@ -214,7 +215,7 @@ void EndController::handleSteering(float dt) 0.5f* QuadGraph::get()->getNode(m_track_node).getPathWidth()+0.5f ) { const int next = m_next_node_index[m_track_node]; - target_point = QuadGraph::get()->getQuadOfNode(next).getCenter(); + target_point = QuadGraph::get()->getNode(next).getCenter(); #ifdef AI_DEBUG Log::debug("end_controller.cpp", "- Outside of road: steer to center point."); #endif @@ -274,7 +275,7 @@ void EndController::findNonCrashingPoint(Vec3 *result) target_sector = m_next_node_index[sector]; //direction is a vector from our kart to the sectors we are testing - direction = QuadGraph::get()->getQuadOfNode(target_sector).getCenter() + direction = QuadGraph::get()->getNode(target_sector).getCenter() - m_kart->getXYZ(); float len=direction.length_2d(); @@ -301,7 +302,7 @@ void EndController::findNonCrashingPoint(Vec3 *result) if ( distance + m_kart_width * 0.5f > QuadGraph::get()->getNode(sector).getPathWidth()*0.5f ) { - *result = QuadGraph::get()->getQuadOfNode(sector).getCenter(); + *result = QuadGraph::get()->getNode(sector).getCenter(); return; } } diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index 5c311258e..49ec74084 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -224,10 +224,7 @@ void SkiddingAI::update(float dt) // This is used to enable firing an item backwards. m_controls->m_look_back = false; m_controls->m_nitro = false; - - //Vec3 gravity = m_kart->getBody()->getGravity(); - //Log::info("Sector", "%f %f %f %d", gravity[0], gravity[1], gravity[2],m_track_node); - + // Don't do anything if there is currently a kart animations shown. if(m_kart->getKartAnimation()) return; @@ -378,7 +375,7 @@ void SkiddingAI::update(float dt) m_controls->m_fire = true; } } - + /*And obviously general kart stuff*/ AIBaseLapController::update(dt); } // update @@ -481,11 +478,11 @@ void SkiddingAI::handleSteering(float dt) if( fabsf(side_dist) > 0.5f* QuadGraph::get()->getNode(m_track_node).getPathWidth()+0.5f ) { - steer_angle = steerToPoint(QuadGraph::get()->getQuadOfNode(next) + steer_angle = steerToPoint(QuadGraph::get()->getNode(next) .getCenter()); #ifdef AI_DEBUG - m_debug_sphere[0]->setPosition(QuadGraph::get()->getQuadOfNode(next) + m_debug_sphere[0]->setPosition(QuadGraph::get()->getNode(next) .getCenter().toIrrVector()); Log::debug(getControllerName().c_str(), "Outside of road: steer to center point."); @@ -622,10 +619,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, #ifdef AI_DEBUG m_item_sphere->setVisible(false); #endif - // Angle of line from kart to aim_point - float kart_aim_angle = atan2(aim_point->getX()-m_kart->getXYZ().getX(), - aim_point->getZ()-m_kart->getXYZ().getZ()); - + // Angle to aim_point Vec3 kart_aim_direction = *aim_point - m_kart->getXYZ(); // Make sure we have a valid last_node @@ -642,9 +636,9 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, const float max_item_lookahead_distance = 30.f; while(distance < max_item_lookahead_distance) { - int q_index= QuadGraph::get()->getNode(node).getQuadIndex(); + int n_index= QuadGraph::get()->getNode(node).getNodeIndex(); const std::vector &items_ahead = - ItemManager::get()->getItemsInQuads(q_index); + ItemManager::get()->getItemsInQuads(n_index); for(unsigned int i=0; i0; - core::line2df line_to_target(aim_point->getX(), - aim_point->getZ(), - m_kart->getXYZ().getX(), - m_kart->getXYZ().getZ()); - - core::line3df line_to_target_3d((*aim_point).toIrrVector(), + core::line3df line_to_target_3d((*aim_point).toIrrVector(), m_kart->getXYZ().toIrrVector()); - + // 2) If the kart is aiming for an item, but (suddenly) detects // some close-by items to avoid (e.g. behind the item, which was too // far away to be considered earlier, or because the item was switched @@ -792,7 +781,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, // so that it can potentially become a permanent target. Vec3 xyz = item_to_collect->getXYZ(); Vec3 item_direction = xyz - m_kart->getXYZ(); - Vec3 plane_normal = QuadGraph::get()->getQuadOfNode(m_track_node) + Vec3 plane_normal = QuadGraph::get()->getNode(m_track_node) .getNormal(); float dist_to_plane = item_direction.dot(plane_normal); Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; @@ -840,7 +829,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, bool SkiddingAI::hitBadItemWhenAimAt(const Item *item, const std::vector &items_to_avoid) { - core::line3df to_item(m_kart->getXYZ().toIrrVector(), + core::line3df to_item(m_kart->getXYZ().toIrrVector(), item->getXYZ().toIrrVector()); for(unsigned int i=0; igetXYZ(); Vec3 item_direction = xyz - m_kart->getXYZ(); - Vec3 plane_normal = QuadGraph::get()->getQuadOfNode(m_track_node) - .getNormal(); + Vec3 plane_normal = QuadGraph::get()->getNode(m_track_node).getNormal(); float dist_to_plane = item_direction.dot(plane_normal); Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; @@ -936,12 +924,9 @@ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, // Check if we would drive left of the leftmost or right of the // rightmost point - if so, nothing to do. - - //core::vector2df left(items_to_avoid[index_left_most]->getXYZ().getX(), - // items_to_avoid[index_left_most]->getXYZ().getZ()); Vec3 left(items_to_avoid[index_left_most]->getXYZ()); int node_index = items_to_avoid[index_left_most]->getGraphNode(); - Vec3 normal = QuadGraph::get()->getQuadOfNode(node_index).getNormal(); + Vec3 normal = QuadGraph::get()->getNode(node_index).getNormal(); Vec3 p1 = line_to_target.start, p2 = line_to_target.getMiddle() + normal.toIrrVector(), p3 = line_to_target.end; @@ -959,11 +944,9 @@ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, } else { - //core::vector2df right(items_to_avoid[index_right_most]->getXYZ().getX(), - // items_to_avoid[index_right_most]->getXYZ().getZ()); Vec3 left(items_to_avoid[index_left_most]->getXYZ()); int node_index = items_to_avoid[index_left_most]->getGraphNode(); - Vec3 normal = QuadGraph::get()->getQuadOfNode(node_index).getNormal(); + Vec3 normal = QuadGraph::get()->getNode(node_index).getNormal(); Vec3 p1 = line_to_target.start, p2 = line_to_target.getMiddle() + normal.toIrrVector(), p3 = line_to_target.end; @@ -1121,11 +1104,10 @@ void SkiddingAI::evaluateItems(const Item *item, Vec3 kart_aim_direction, // the kart is. The current quad provides a good estimate of the kart's plane. const Vec3 &xyz = item->getXYZ(); Vec3 item_direction = xyz - m_kart->getXYZ(); - Vec3 plane_normal = QuadGraph::get()->getQuadOfNode(m_track_node) - .getNormal(); + Vec3 plane_normal = QuadGraph::get()->getNode(m_track_node).getNormal(); float dist_to_plane = item_direction.dot(plane_normal); Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; - + float angle_to_item = (projected_xyz - m_kart->getXYZ()) .angle(kart_aim_direction); @@ -1879,23 +1861,23 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node) *last_node = m_next_node_index[m_track_node]; const core::vector2df xz = m_kart->getXYZ().toIrrVector2d(); - const Quad &q = QuadGraph::get()->getQuadOfNode(*last_node); + const GraphNode &g = QuadGraph::get()->getNode(*last_node); // Index of the left and right end of a quad. const unsigned int LEFT_END_POINT = 0; const unsigned int RIGHT_END_POINT = 1; - core::line2df left (xz, q[LEFT_END_POINT ].toIrrVector2d()); - core::line2df right(xz, q[RIGHT_END_POINT].toIrrVector2d()); + core::line2df left (xz, g[LEFT_END_POINT ].toIrrVector2d()); + core::line2df right(xz, g[RIGHT_END_POINT].toIrrVector2d()); #if defined(AI_DEBUG) && defined(AI_DEBUG_NEW_FIND_NON_CRASHING) const Vec3 eps1(0,0.5f,0); m_curve[CURVE_LEFT]->clear(); m_curve[CURVE_LEFT]->addPoint(m_kart->getXYZ()+eps1); - m_curve[CURVE_LEFT]->addPoint(q[LEFT_END_POINT]+eps1); + m_curve[CURVE_LEFT]->addPoint(g[LEFT_END_POINT]+eps1); m_curve[CURVE_LEFT]->addPoint(m_kart->getXYZ()+eps1); m_curve[CURVE_RIGHT]->clear(); m_curve[CURVE_RIGHT]->addPoint(m_kart->getXYZ()+eps1); - m_curve[CURVE_RIGHT]->addPoint(q[RIGHT_END_POINT]+eps1); + m_curve[CURVE_RIGHT]->addPoint(g[RIGHT_END_POINT]+eps1); m_curve[CURVE_RIGHT]->addPoint(m_kart->getXYZ()+eps1); #endif #if defined(AI_DEBUG_KART_HEADING) || defined(AI_DEBUG_NEW_FIND_NON_CRASHING) @@ -1908,13 +1890,13 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node) while(1) { unsigned int next_sector = m_next_node_index[*last_node]; - const Quad &q_next = QuadGraph::get()->getQuadOfNode(next_sector); + const GraphNode &g_next = QuadGraph::get()->getNode(next_sector); // Test if the next left point is to the right of the left // line. If so, a new left line is defined. - if(left.getPointOrientation(q_next[LEFT_END_POINT].toIrrVector2d()) + if(left.getPointOrientation(g_next[LEFT_END_POINT].toIrrVector2d()) < 0 ) { - core::vector2df p = q_next[LEFT_END_POINT].toIrrVector2d(); + core::vector2df p = g_next[LEFT_END_POINT].toIrrVector2d(); // Stop if the new point is to the right of the right line if(right.getPointOrientation(p)<0) break; @@ -1930,10 +1912,10 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node) // Test if new right point is to the left of the right line. If // so, a new right line is defined. - if(right.getPointOrientation(q_next[RIGHT_END_POINT].toIrrVector2d()) + if(right.getPointOrientation(g_next[RIGHT_END_POINT].toIrrVector2d()) > 0 ) { - core::vector2df p = q_next[RIGHT_END_POINT].toIrrVector2d(); + core::vector2df p = g_next[RIGHT_END_POINT].toIrrVector2d(); // Break if new point is to the left of left line if(left.getPointOrientation(p)>0) break; @@ -1955,7 +1937,7 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node) // 0.5f*(left.end.Y+right.end.Y)); //*result = ppp; - *result = QuadGraph::get()->getQuadOfNode(*last_node).getCenter(); + *result = QuadGraph::get()->getNode(*last_node).getCenter(); } // findNonCrashingPointNew //----------------------------------------------------------------------------- @@ -1992,10 +1974,10 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) target_sector = m_next_node_index[*last_node]; //direction is a vector from our kart to the sectors we are testing - direction = QuadGraph::get()->getQuadOfNode(target_sector).getCenter() + direction = QuadGraph::get()->getNode(target_sector).getCenter() - m_kart->getXYZ(); - float len=direction.length_2d(); + float len=direction.length(); unsigned int steps = (unsigned int)( len / m_kart_length ); if( steps < 3 ) steps = 3; @@ -2024,20 +2006,28 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) if ( distance + m_kart_width * 0.5f > QuadGraph::get()->getNode(*last_node).getPathWidth()*0.5f ) { - *aim_position = QuadGraph::get()->getQuadOfNode(*last_node) + *aim_position = QuadGraph::get()->getNode(*last_node) .getCenter(); return; } } *last_node = target_sector; } // for i<100 - *aim_position = QuadGraph::get()->getQuadOfNode(*last_node).getCenter(); + *aim_position = QuadGraph::get()->getNode(*last_node).getCenter(); } // findNonCrashingPointFixed //----------------------------------------------------------------------------- /** This is basically the original AI algorithm. It is clearly buggy: * 1. the test: * + * distance + m_kart_width * 0.5f + * > QuadGraph::get()->getNode(*last_node).getPathWidth() ) + * + * is incorrect, it should compare with getPathWith*0.5f (since distance + * is the distance from the center, i.e. it is half the path width if + * the point is at the edge). + * 2. the test: + * * QuadGraph::get()->spatialToTrack(&step_track_coord, step_coord, * *last_node ); * in the for loop tests always against distance from the same @@ -2050,17 +2040,10 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) * which takes some time - so it is actually mostly on track. * Since this algoritm (so far) ends up with by far the best AI behaviour, * it is for now the default). - * - * GSoC 2014: This algorithm has been updated so that the AI can drive on - * upside-down tracks. Because steering only works in 2D, you can steer left and - * right but not up and down, but the track might not be 2D. So for every quad, - * we have a set of k unrolled quads starting from the current quad. The AI uses - * these unrolled quads to search for a suitable aim point. - * * \param aim_position On exit contains the point the AI should aim at. * \param last_node On exit contais the graph node the AI is aiming at. */ -void SkiddingAI::findNonCrashingPoint(Vec3 *aim_position, int *last_node) + void SkiddingAI::findNonCrashingPoint(Vec3 *aim_position, int *last_node) { #ifdef AI_DEBUG_KART_HEADING const Vec3 eps(0,0.5f,0); @@ -2069,7 +2052,7 @@ void SkiddingAI::findNonCrashingPoint(Vec3 *aim_position, int *last_node) Vec3 forw(0, 0, 50); m_curve[CURVE_KART]->addPoint(m_kart->getTrans()(forw)+eps); #endif - *last_node = m_track_node; + *last_node = m_next_node_index[m_track_node]; float angle = QuadGraph::get()->getAngleToNext(m_track_node, m_successor_index[m_track_node]); int target_sector; @@ -2078,30 +2061,14 @@ void SkiddingAI::findNonCrashingPoint(Vec3 *aim_position, int *last_node) Vec3 step_track_coord; float angle1; - - // We need to find out if there is an alternate path that the kart might take. - // This is done in advance so that the right set of unrolled quads are used - // and the AI finds aim_points on the alternate path. future_successor_idx - // is successor index of a future node. 0 is the default, but if there is an - // alternate path coming up, this will be set to 1. - int future_successor_idx = 0; - int current = m_track_node; - for (unsigned int j = 0; j < QuadGraph::get()->getNumberOfUnrolledQuads(); j++) - { - if (m_successor_index[current] != 0) - future_successor_idx = m_successor_index[current]; - - current = m_next_node_index[current]; - } - - // This for loop runs till it runs out of unrolled quads or breaks when the - // aim point is outside one of the unrolled quads. - for(unsigned int j=0; jgetNumberOfUnrolledQuads(); j++) + // The original while(1) loop is replaced with a for loop to avoid + // infinite loops (which we had once or twice). Usually the number + // of iterations in the while loop is less than 7. + for(unsigned int j=0; j<100; j++) { // target_sector is the sector at the longest distance that we can // drive to without crashing with the track. target_sector = m_next_node_index[*last_node]; - angle1 = QuadGraph::get()->getAngleToNext(target_sector, m_successor_index[target_sector]); // In very sharp turns this algorithm tends to aim at off track points, @@ -2110,17 +2077,16 @@ void SkiddingAI::findNonCrashingPoint(Vec3 *aim_position, int *last_node) float diff = normalizeAngle(angle1-angle); if(fabsf(diff)>1.5f) { - *aim_position = QuadGraph::get()->getQuadOfNode(target_sector) - .getCenter(); + *aim_position = QuadGraph::get()->getNode(target_sector) + .getCenter(); return; } - Quad target_quad_unrolled = QuadGraph::get()->getNode(m_track_node). - getUnrolledQuad(future_successor_idx,j + 1); + //direction is a vector from our kart to the sectors we are testing + direction = QuadGraph::get()->getNode(target_sector).getCenter() + - m_kart->getXYZ(); - direction = target_quad_unrolled.getCenter() - m_kart->getXYZ(); - - float len = direction.length(); + float len=direction.length(); unsigned int steps = (unsigned int)( len / m_kart_length ); if( steps < 3 ) steps = 3; @@ -2130,38 +2096,34 @@ void SkiddingAI::findNonCrashingPoint(Vec3 *aim_position, int *last_node) if( steps>1000) steps = 1000; // Protection against having vel_normal with nan values - if(len>0.0f) - { + if(len>0.0f) { direction*= 1.0f/len; } Vec3 step_coord; - //Test if we crash if we drive towards the target sector (unrolled set) + //Test if we crash if we drive towards the target sector for(unsigned int i = 2; i < steps; ++i ) { step_coord = m_kart->getXYZ()+direction*m_kart_length * float(i); - QuadGraph::get()->spatialToTrackUnrolled(&step_track_coord, step_coord, - m_track_node, j, future_successor_idx); + QuadGraph::get()->spatialToTrack(&step_track_coord, step_coord, + *last_node ); - float distance = step_track_coord[0]; + float distance = fabsf(step_track_coord[0]); //If we are outside, the previous node is what we are looking for if ( distance + m_kart_width * 0.5f - > QuadGraph::get()->getNode((*last_node+j)%QuadGraph::get()->getNumNodes() ).getPathWidth()*0.5f ) + > QuadGraph::get()->getNode(*last_node).getPathWidth() ) { - *aim_position = QuadGraph::get()->getUnrolledQuadOfNode(m_track_node,future_successor_idx,j+1) + *aim_position = QuadGraph::get()->getNode(*last_node) .getCenter(); return; } } angle = angle1; *last_node = target_sector; - } - // If we are inside entire unrolled set then set the aim_point to be center of the last unrolled quad - *aim_position = QuadGraph::get()->getNode(m_track_node). - getUnrolledQuad(future_successor_idx,QuadGraph::get()->getNumberOfUnrolledQuads()).getCenter(); - + } // for i<100 + *aim_position = QuadGraph::get()->getNode(*last_node).getCenter(); } // findNonCrashingPoint //----------------------------------------------------------------------------- @@ -2176,9 +2138,9 @@ void SkiddingAI::determineTrackDirection() //float angle_to_track = qg->getNode(m_track_node).getAngleToSuccessor(succ) // - m_kart->getHeading(); - Vec3 track_direction = -qg->getQuadOfNode(m_track_node).getCenter() - + qg->getQuadOfNode(next).getCenter(); - //Vec3 kart_direction = qg->getQuadOfNode(m_track_node).getCenter() + m_kart->getVelocity(); + Vec3 track_direction = -qg->getNode(m_track_node).getCenter() + + qg->getNode(next).getCenter(); + //Vec3 kart_direction = qg->getNode(m_track_node).getCenter() + m_kart->getVelocity(); float angle_to_track = 0; if (m_kart->getVelocity().length() > 0.0f) @@ -2203,8 +2165,6 @@ void SkiddingAI::determineTrackDirection() return; } - - qg->getNode(next).getDirectionData(m_successor_index[next], &m_current_track_direction, &m_last_direction_node); @@ -2265,10 +2225,10 @@ void SkiddingAI::handleCurve() // Pick either the lower left or right point: int index = m_current_track_direction==GraphNode::DIR_LEFT ? 0 : 1; - float r = (m_curve_center - qg->getQuadOfNode(i)[index]).length(); + float r = (m_curve_center - qg->getNode(i)[index]).length(); if(m_current_curve_radius < r) { - last_xyz = qg->getQuadOfNode(i)[index]; + last_xyz = qg->getNode(i)[index]; determineTurnRadius(xyz, tangent, last_xyz, &m_curve_center, &m_current_curve_radius); m_last_direction_node = i; diff --git a/src/karts/controller/skidding_ai.hpp b/src/karts/controller/skidding_ai.hpp index 7762c3ba4..cfbbd4863 100644 --- a/src/karts/controller/skidding_ai.hpp +++ b/src/karts/controller/skidding_ai.hpp @@ -31,15 +31,15 @@ #ifdef DEBUG // Enable AI graphical debugging -# undef AI_DEBUG +# define AI_DEBUG // Shows left and right lines when using new findNonCrashing function -# undef AI_DEBUG_NEW_FIND_NON_CRASHING +# define AI_DEBUG_NEW_FIND_NON_CRASHING // Show the predicted turn circles -# undef AI_DEBUG_CIRCLES +# define AI_DEBUG_CIRCLES // Show the heading of the kart -# undef AI_DEBUG_KART_HEADING +# define AI_DEBUG_KART_HEADING // Shows line from kart to its aim point -# undef AI_DEBUG_KART_AIM +# define AI_DEBUG_KART_AIM #endif diff --git a/src/karts/controller/test_ai.cpp b/src/karts/controller/test_ai.cpp index 8033db4b4..15cf5303f 100644 --- a/src/karts/controller/test_ai.cpp +++ b/src/karts/controller/test_ai.cpp @@ -484,11 +484,11 @@ void SkiddingAI::handleSteering(float dt) if( fabsf(side_dist) > 0.5f* QuadGraph::get()->getNode(m_track_node).getPathWidth()+0.5f ) { - steer_angle = steerToPoint(QuadGraph::get()->getQuadOfNode(next) + steer_angle = steerToPoint(QuadGraph::get()->getNode(next) .getCenter()); #ifdef AI_DEBUG - m_debug_sphere[0]->setPosition(QuadGraph::get()->getQuadOfNode(next) + m_debug_sphere[0]->setPosition(QuadGraph::get()->getNode(next) .getCenter().toIrrVector()); Log::debug(getControllerName().c_str(), "Outside of road: steer to center point."); @@ -621,13 +621,12 @@ void SkiddingAI::handleSteering(float dt) */ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, int last_node) -{/* +{ #ifdef AI_DEBUG m_item_sphere->setVisible(false); #endif - // Angle of line from kart to aim_point - float kart_aim_angle = atan2(aim_point->getX()-m_kart->getXYZ().getX(), - aim_point->getZ()-m_kart->getXYZ().getZ()); + // Angle to aim_point + Vec3 kart_aim_direction = *aim_point - m_kart->getXYZ(); // Make sure we have a valid last_node if(last_node==QuadGraph::UNKNOWN_SECTOR) @@ -643,12 +642,12 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, const float max_item_lookahead_distance = 30.f; while(distance < max_item_lookahead_distance) { - int q_index= QuadGraph::get()->getNode(node).getQuadIndex(); + int n_index= QuadGraph::get()->getNode(node).getNodeIndex(); const std::vector &items_ahead = - ItemManager::get()->getItemsInQuads(q_index); + ItemManager::get()->getItemsInQuads(n_index); for(unsigned int i=0; igetDistanceToNext(node, @@ -660,10 +659,8 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, m_avoid_item_close = items_to_avoid.size()>0; - core::line2df line_to_target(aim_point->getX(), - aim_point->getZ(), - m_kart->getXYZ().getX(), - m_kart->getXYZ().getZ()); + core::line3df line_to_target_3d((*aim_point).toIrrVector(), + m_kart->getXYZ().toIrrVector()); // 2) If the kart is aiming for an item, but (suddenly) detects // some close-by items to avoid (e.g. behind the item, which was too @@ -676,7 +673,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, for(unsigned int i=0; i< items_to_avoid.size(); i++) { Vec3 d = items_to_avoid[i]->getXYZ()-m_item_to_collect->getXYZ(); - if( d.length2_2d()>m_ai_properties->m_bad_item_closeness_2) + if( d.length2()>m_ai_properties->m_bad_item_closeness_2) continue; // It could make sense to also test if the bad item would // actually be hit, not only if it is close (which can result @@ -696,7 +693,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, // ------------------------------------- if(m_item_to_collect) { - if(handleSelectedItem(kart_aim_angle, aim_point)) + if(handleSelectedItem(kart_aim_direction, aim_point)) { // Still aim at the previsouly selected item. *aim_point = m_item_to_collect->getXYZ(); @@ -721,7 +718,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, { // If we need to steer to avoid an item, this takes priority, // ignore items to collect and return the new aim_point. - if(steerToAvoid(items_to_avoid, line_to_target, aim_point)) + if(steerToAvoid(items_to_avoid, line_to_target_3d, aim_point)) { #ifdef AI_DEBUG m_item_sphere->setVisible(true); @@ -770,7 +767,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, // it's on a good enough driveline, so make this item a permanent // target. Otherwise only try to get closer (till hopefully this // item s on our driveline) - if(item_to_collect->hitLine(line_to_target, m_kart)) + if(item_to_collect->hitLine(line_to_target_3d, m_kart)) { #ifdef AI_DEBUG m_item_sphere->setVisible(true); @@ -789,9 +786,15 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, // Kart will not hit item, try to get closer to this item // so that it can potentially become a permanent target. Vec3 xyz = item_to_collect->getXYZ(); - float item_angle = atan2(xyz.getX() - m_kart->getXYZ().getX(), - xyz.getZ() - m_kart->getXYZ().getZ()); - float angle = normalizeAngle(kart_aim_angle - item_angle); + Vec3 item_direction = xyz - m_kart->getXYZ(); + Vec3 plane_normal = QuadGraph::get()->getNode(m_track_node) + .getNormal(); + float dist_to_plane = item_direction.dot(plane_normal); + Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; + + float angle_to_item = (projected_xyz - m_kart->getXYZ()) + .angle(kart_aim_direction); + float angle = normalizeAngle(angle_to_item); if(fabsf(angle) < 0.3) { @@ -818,7 +821,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, } // kart will not hit item } // does hit hit bad item } // if item to consider was found -*/ + } // handleItemCollectionAndAvoidance //----------------------------------------------------------------------------- @@ -832,13 +835,13 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, bool SkiddingAI::hitBadItemWhenAimAt(const Item *item, const std::vector &items_to_avoid) { -/* core::line2df to_item(m_kart->getXYZ().getX(), m_kart->getXYZ().getZ(), - item->getXYZ().getX(), item->getXYZ().getZ() ); + core::line3df to_item(m_kart->getXYZ().toIrrVector(), + item->getXYZ().toIrrVector()); for(unsigned int i=0; ihitLine(to_item, m_kart)) return true; - }*/ + } return false; } // hitBadItemWhenAimAt @@ -854,7 +857,7 @@ bool SkiddingAI::hitBadItemWhenAimAt(const Item *item, * \param last_node * \return True if th AI should still aim for the pre-selected item. */ -bool SkiddingAI::handleSelectedItem(float kart_aim_angle, Vec3 *aim_point) +bool SkiddingAI::handleSelectedItem(Vec3 kart_aim_direction, Vec3 *aim_point) { // If the item is unavailable keep on testing. It is not necessary // to test if an item has turned bad, this was tested before this @@ -862,11 +865,21 @@ bool SkiddingAI::handleSelectedItem(float kart_aim_angle, Vec3 *aim_point) if(m_item_to_collect->getDisableTime()>0) return false; + // Project the item's location onto the plane of the current quad. + // This is necessary because the kart's aim point may not be on the track + // in 3D curves. So we project the item's location onto the plane in which + // the kart is. The current quad provides a good estimate of the kart's plane. const Vec3 &xyz = m_item_to_collect->getXYZ(); - float item_angle = atan2(xyz.getX() - m_kart->getXYZ().getX(), - xyz.getZ() - m_kart->getXYZ().getZ()); + Vec3 item_direction = xyz - m_kart->getXYZ(); + Vec3 plane_normal = QuadGraph::get()->getNode(m_track_node).getNormal(); + float dist_to_plane = item_direction.dot(plane_normal); + Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; + + float angle_to_item = (projected_xyz - m_kart->getXYZ()) + .angle(kart_aim_direction); + + float angle = normalizeAngle(angle_to_item); - float angle = normalizeAngle(kart_aim_angle - item_angle); if(fabsf(angle)>1.5) { // We (likely) have passed the item we were aiming for @@ -891,10 +904,10 @@ bool SkiddingAI::handleSelectedItem(float kart_aim_angle, Vec3 *aim_point) * \return True if steering is necessary to avoid an item. */ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, - const core::line2df &line_to_target, + const core::line3df &line_to_target, Vec3 *aim_point) { -/* // First determine the left-most and right-most item. + // First determine the left-most and right-most item. float left_most = items_to_avoid[0]->getDistanceFromCenter(); float right_most = items_to_avoid[0]->getDistanceFromCenter(); int index_left_most = 0; @@ -917,14 +930,19 @@ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, // Check if we would drive left of the leftmost or right of the // rightmost point - if so, nothing to do. - core::vector2df left(items_to_avoid[index_left_most]->getXYZ().getX(), - items_to_avoid[index_left_most]->getXYZ().getZ()); + Vec3 left(items_to_avoid[index_left_most]->getXYZ()); + int node_index = items_to_avoid[index_left_most]->getGraphNode(); + Vec3 normal = QuadGraph::get()->getNode(node_index).getNormal(); + Vec3 p1 = line_to_target.start, + p2 = line_to_target.getMiddle() + normal.toIrrVector(), + p3 = line_to_target.end; + int item_index = -1; bool is_left = false; // >=0 means the point is to the right of the line, or the line is // to the left of the point. - if(line_to_target.getPointOrientation(left)>=0) + if(left.sideofPlane(p1,p2,p3) <= 0) { // Left of leftmost point item_index = index_left_most; @@ -932,9 +950,14 @@ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, } else { - core::vector2df right(items_to_avoid[index_right_most]->getXYZ().getX(), - items_to_avoid[index_right_most]->getXYZ().getZ()); - if(line_to_target.getPointOrientation(right)<=0) + Vec3 left(items_to_avoid[index_left_most]->getXYZ()); + int node_index = items_to_avoid[index_left_most]->getGraphNode(); + Vec3 normal = QuadGraph::get()->getNode(node_index).getNormal(); + Vec3 p1 = line_to_target.start, + p2 = line_to_target.getMiddle() + normal.toIrrVector(), + p3 = line_to_target.end; + + if (left.sideofPlane(p1, p2, p3) >= 0) { // Right of rightmost point item_index = index_right_most; @@ -975,20 +998,20 @@ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, float min_distance[2] = {99999.9f, 99999.9f}; int index[2] = {-1, -1}; - core::vector2df closest2d[2]; + core::vector3df closest3d[2]; for(unsigned int i=0; igetXYZ(); core::vector2df item2d = xyz.toIrrVector2d(); - core::vector2df point2d = line_to_target.getClosestPoint(item2d); - float d = (xyz.toIrrVector2d() - point2d).getLengthSQ(); - float direction = line_to_target.getPointOrientation(item2d); - int ind = direction<0 ? 0 : 1; + core::vector3df point3d = line_to_target.getClosestPoint(xyz.toIrrVector()); + float d = (xyz.toIrrVector() - point3d).getLengthSQ(); + float direction = xyz.sideofPlane(p1,p2,p3); + int ind = direction<0 ? 1 : 0; if(d &items_to_avoid, // We are driving between item_to_avoid[index[0]] and ...[1]. // If we don't hit any of them, just keep on driving as normal - bool hit_left = items_to_avoid[index[0]]->hitKart(closest2d[0], m_kart); - bool hit_right = items_to_avoid[index[1]]->hitKart(closest2d[1], m_kart); + bool hit_left = items_to_avoid[index[0]]->hitKart(closest3d[0], m_kart); + bool hit_right = items_to_avoid[index[1]]->hitKart(closest3d[1], m_kart); if( !hit_left && !hit_right) return false; -*/ + // If we hit the left item, aim at the right avoidance point // of the left item. We might still hit the right item ... this might // still be better than going too far off track - //if(hit_left) - //{ - // *aim_point = - // *(items_to_avoid[index[0]]->getAvoidancePoint(/*left*/false)); - // return true; - //} + if(hit_left) + { + *aim_point = + *(items_to_avoid[index[0]]->getAvoidancePoint(/*left*/false)); + return true; + } // Now we must be hitting the right item, so try aiming at the left // avoidance point of the right item. - //*aim_point = *(items_to_avoid[index[1]]->getAvoidancePoint(/*left*/true)); - + *aim_point = *(items_to_avoid[index[1]]->getAvoidancePoint(/*left*/true)); return true; } // steerToAvoid @@ -1034,7 +1056,7 @@ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, * (NULL if no item was avoided so far). * \param item_to_collect A pointer to a previously selected item to collect. */ -void SkiddingAI::evaluateItems(const Item *item, float kart_aim_angle, +void SkiddingAI::evaluateItems(const Item *item, Vec3 kart_aim_direction, std::vector *items_to_avoid, std::vector *items_to_collect) { @@ -1082,13 +1104,20 @@ void SkiddingAI::evaluateItems(const Item *item, float kart_aim_angle, // to avoid are collected). if(!avoid) { - // item_angle The angle of the item (relative to the forward axis, - // so 0 means straight ahead in world coordinates!). + // Project the item's location onto the plane of the current quad. + // This is necessary because the kart's aim point may not be on the track + // in 3D curves. So we project the item's location onto the plane in which + // the kart is. The current quad provides a good estimate of the kart's plane. const Vec3 &xyz = item->getXYZ(); - float item_angle = atan2(xyz.getX() - m_kart->getXYZ().getX(), - xyz.getZ() - m_kart->getXYZ().getZ()); + Vec3 item_direction = xyz - m_kart->getXYZ(); + Vec3 plane_normal = QuadGraph::get()->getNode(m_track_node).getNormal(); + float dist_to_plane = item_direction.dot(plane_normal); + Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; - float diff = normalizeAngle(kart_aim_angle-item_angle); + float angle_to_item = (projected_xyz - m_kart->getXYZ()) + .angle(kart_aim_direction); + + float diff = normalizeAngle(angle_to_item); // The kart is driving at high speed, when the current max speed // is higher than the max speed of the kart (which is caused by @@ -1117,14 +1146,14 @@ void SkiddingAI::evaluateItems(const Item *item, float kart_aim_angle, else list = items_to_collect; - float new_distance = (item->getXYZ() - m_kart->getXYZ()).length2_2d(); + float new_distance = (item->getXYZ() - m_kart->getXYZ()).length2(); // This list is usually very short, so use a simple bubble sort list->push_back(item); int i; for(i=(int)list->size()-2; i>=0; i--) { - float d = ((*list)[i]->getXYZ() - m_kart->getXYZ()).length2_2d(); + float d = ((*list)[i]->getXYZ() - m_kart->getXYZ()).length2(); if(d<=new_distance) { break; @@ -1881,23 +1910,23 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node) *last_node = m_next_node_index[m_track_node]; const core::vector2df xz = m_kart->getXYZ().toIrrVector2d(); - const Quad &q = QuadGraph::get()->getQuadOfNode(*last_node); + const GraphNode &g = QuadGraph::get()->getNode(*last_node); // Index of the left and right end of a quad. const unsigned int LEFT_END_POINT = 0; const unsigned int RIGHT_END_POINT = 1; - core::line2df left (xz, q[LEFT_END_POINT ].toIrrVector2d()); - core::line2df right(xz, q[RIGHT_END_POINT].toIrrVector2d()); + core::line2df left (xz, g[LEFT_END_POINT ].toIrrVector2d()); + core::line2df right(xz, g[RIGHT_END_POINT].toIrrVector2d()); #if defined(AI_DEBUG) && defined(AI_DEBUG_NEW_FIND_NON_CRASHING) const Vec3 eps1(0,0.5f,0); m_curve[CURVE_LEFT]->clear(); m_curve[CURVE_LEFT]->addPoint(m_kart->getXYZ()+eps1); - m_curve[CURVE_LEFT]->addPoint(q[LEFT_END_POINT]+eps1); + m_curve[CURVE_LEFT]->addPoint(g[LEFT_END_POINT]+eps1); m_curve[CURVE_LEFT]->addPoint(m_kart->getXYZ()+eps1); m_curve[CURVE_RIGHT]->clear(); m_curve[CURVE_RIGHT]->addPoint(m_kart->getXYZ()+eps1); - m_curve[CURVE_RIGHT]->addPoint(q[RIGHT_END_POINT]+eps1); + m_curve[CURVE_RIGHT]->addPoint(g[RIGHT_END_POINT]+eps1); m_curve[CURVE_RIGHT]->addPoint(m_kart->getXYZ()+eps1); #endif #if defined(AI_DEBUG_KART_HEADING) || defined(AI_DEBUG_NEW_FIND_NON_CRASHING) @@ -1910,13 +1939,13 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node) while(1) { unsigned int next_sector = m_next_node_index[*last_node]; - const Quad &q_next = QuadGraph::get()->getQuadOfNode(next_sector); + const GraphNode &g_next = QuadGraph::get()->getNode(next_sector); // Test if the next left point is to the right of the left // line. If so, a new left line is defined. - if(left.getPointOrientation(q_next[LEFT_END_POINT].toIrrVector2d()) + if(left.getPointOrientation(g_next[LEFT_END_POINT].toIrrVector2d()) < 0 ) { - core::vector2df p = q_next[LEFT_END_POINT].toIrrVector2d(); + core::vector2df p = g_next[LEFT_END_POINT].toIrrVector2d(); // Stop if the new point is to the right of the right line if(right.getPointOrientation(p)<0) break; @@ -1932,10 +1961,10 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node) // Test if new right point is to the left of the right line. If // so, a new right line is defined. - if(right.getPointOrientation(q_next[RIGHT_END_POINT].toIrrVector2d()) + if(right.getPointOrientation(g_next[RIGHT_END_POINT].toIrrVector2d()) > 0 ) { - core::vector2df p = q_next[RIGHT_END_POINT].toIrrVector2d(); + core::vector2df p = g_next[RIGHT_END_POINT].toIrrVector2d(); // Break if new point is to the left of left line if(left.getPointOrientation(p)>0) break; @@ -1957,7 +1986,7 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node) // 0.5f*(left.end.Y+right.end.Y)); //*result = ppp; - *result = QuadGraph::get()->getQuadOfNode(*last_node).getCenter(); + *result = QuadGraph::get()->getNode(*last_node).getCenter(); } // findNonCrashingPointNew //----------------------------------------------------------------------------- @@ -1994,10 +2023,10 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) target_sector = m_next_node_index[*last_node]; //direction is a vector from our kart to the sectors we are testing - direction = QuadGraph::get()->getQuadOfNode(target_sector).getCenter() + direction = QuadGraph::get()->getNode(target_sector).getCenter() - m_kart->getXYZ(); - float len=direction.length_2d(); + float len=direction.length(); unsigned int steps = (unsigned int)( len / m_kart_length ); if( steps < 3 ) steps = 3; @@ -2026,14 +2055,14 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) if ( distance + m_kart_width * 0.5f > QuadGraph::get()->getNode(*last_node).getPathWidth()*0.5f ) { - *aim_position = QuadGraph::get()->getQuadOfNode(*last_node) + *aim_position = QuadGraph::get()->getNode(*last_node) .getCenter(); return; } } *last_node = target_sector; } // for i<100 - *aim_position = QuadGraph::get()->getQuadOfNode(*last_node).getCenter(); + *aim_position = QuadGraph::get()->getNode(*last_node).getCenter(); } // findNonCrashingPointFixed //----------------------------------------------------------------------------- @@ -2097,16 +2126,16 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) float diff = normalizeAngle(angle1-angle); if(fabsf(diff)>1.5f) { - *aim_position = QuadGraph::get()->getQuadOfNode(target_sector) - .getCenter(); + *aim_position = QuadGraph::get()->getNode(target_sector) + .getCenter(); return; } //direction is a vector from our kart to the sectors we are testing - direction = QuadGraph::get()->getQuadOfNode(target_sector).getCenter() + direction = QuadGraph::get()->getNode(target_sector).getCenter() - m_kart->getXYZ(); - float len=direction.length_2d(); + float len=direction.length(); unsigned int steps = (unsigned int)( len / m_kart_length ); if( steps < 3 ) steps = 3; @@ -2135,7 +2164,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) if ( distance + m_kart_width * 0.5f > QuadGraph::get()->getNode(*last_node).getPathWidth() ) { - *aim_position = QuadGraph::get()->getQuadOfNode(*last_node) + *aim_position = QuadGraph::get()->getNode(*last_node) .getCenter(); return; } @@ -2143,7 +2172,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) angle = angle1; *last_node = target_sector; } // for i<100 - *aim_position = QuadGraph::get()->getQuadOfNode(*last_node).getCenter(); + *aim_position = QuadGraph::get()->getNode(*last_node).getCenter(); } // findNonCrashingPoint //----------------------------------------------------------------------------- @@ -2154,8 +2183,18 @@ void SkiddingAI::determineTrackDirection() { const QuadGraph *qg = QuadGraph::get(); unsigned int succ = m_successor_index[m_track_node]; - float angle_to_track = qg->getNode(m_track_node).getAngleToSuccessor(succ) - - m_kart->getHeading(); + unsigned int next = qg->getNode(m_track_node).getSuccessor(succ); + + //float angle_to_track = qg->getNode(m_track_node).getAngleToSuccessor(succ) + // - m_kart->getHeading(); + Vec3 track_direction = -qg->getNode(m_track_node).getCenter() + + qg->getNode(next).getCenter(); + //Vec3 kart_direction = qg->getNode(m_track_node).getCenter() + m_kart->getVelocity(); + + float angle_to_track = 0; + if (m_kart->getVelocity().length() > 0.0f) + angle_to_track = track_direction.angle(m_kart->getVelocity().normalized()); + angle_to_track = normalizeAngle(angle_to_track); // In certain circumstances (esp. S curves) it is possible that the @@ -2175,8 +2214,6 @@ void SkiddingAI::determineTrackDirection() return; } - unsigned int next = qg->getNode(m_track_node).getSuccessor(succ); - qg->getNode(next).getDirectionData(m_successor_index[next], &m_current_track_direction, &m_last_direction_node); @@ -2237,10 +2274,10 @@ void SkiddingAI::handleCurve() // Pick either the lower left or right point: int index = m_current_track_direction==GraphNode::DIR_LEFT ? 0 : 1; - float r = (m_curve_center - qg->getQuadOfNode(i)[index]).length(); + float r = (m_curve_center - qg->getNode(i)[index]).length(); if(m_current_curve_radius < r) { - last_xyz = qg->getQuadOfNode(i)[index]; + last_xyz = qg->getNode(i)[index]; determineTurnRadius(xyz, tangent, last_xyz, &m_curve_center, &m_current_curve_radius); m_last_direction_node = i; diff --git a/src/karts/controller/test_ai.hpp b/src/karts/controller/test_ai.hpp index 7befd2b7a..8d1ffb277 100644 --- a/src/karts/controller/test_ai.hpp +++ b/src/karts/controller/test_ai.hpp @@ -66,7 +66,6 @@ namespace irr * stage) identical to the Skidding AI. \ingroup controller */ - class TestAI : public AIBaseLapController { private: @@ -205,13 +204,13 @@ private: void computeNearestKarts(); void handleItemCollectionAndAvoidance(Vec3 *aim_point, int last_node); - bool handleSelectedItem(float kart_aim_angle, Vec3 *aim_point); + bool handleSelectedItem(Vec3 kart_aim_direction, Vec3 *aim_point); bool steerToAvoid(const std::vector &items_to_avoid, - const core::line2df &line_to_target, + const core::line3df &line_to_target, Vec3 *aim_point); bool hitBadItemWhenAimAt(const Item *item, const std::vector &items_to_avoid); - void evaluateItems(const Item *item, float kart_aim_angle, + void evaluateItems(const Item *item, Vec3 kart_aim_direction, std::vector *items_to_avoid, std::vector *items_to_collect); diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index e07932147..597fe1136 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -64,6 +64,8 @@ #include "physics/physics.hpp" #include "race/history.hpp" #include "tracks/terrain_info.hpp" +#include "tracks/quad_graph.hpp" +#include "tracks/graph_node.hpp" #include "tracks/track.hpp" #include "tracks/track_manager.hpp" #include "utils/constants.hpp" @@ -1292,7 +1294,7 @@ void Kart::update(float dt) if (QuadGraph::get()) { sector = ((LinearWorld*)World::getWorld())->getTrackSector(getWorldKartId()).getCurrentGraphNode(); - quadNormal = QuadGraph::get()->getQuadOfNode(sector).getNormal(); + quadNormal = QuadGraph::get()->getNode(sector).getNormal(); btQuaternion q = getTrans().getRotation(); float roll = quadNormal.angle((Vec3(0, 1, 0).rotate(q.getAxis(), q.getAngle()))); @@ -1374,7 +1376,7 @@ void Kart::update(float dt) // We do this for now because dist_to_sector is not defined float dist_to_sector; if (QuadGraph::get()) - dist_to_sector = getXYZ().distance(QuadGraph::get()->getQuadOfNode(sector).getCenter()); + dist_to_sector = getXYZ().distance(QuadGraph::get()->getNode(sector).getCenter()); else dist_to_sector = 0; @@ -2421,7 +2423,7 @@ void Kart::updateSliding() if (QuadGraph::get()) { int sector = ((LinearWorld*)World::getWorld())->getTrackSector(getWorldKartId()).getCurrentGraphNode(); - Vec3 quadNormal = QuadGraph::get()->getQuadOfNode(sector).getNormal(); + Vec3 quadNormal = QuadGraph::get()->getNode(sector).getNormal(); Vec3 kart_up = m.getColumn(1); distanceFromUp = kart_up.dot(quadNormal); } diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index 0d880c597..ca72db1f0 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -26,9 +26,12 @@ #include "karts/abstract_kart.hpp" #include "karts/controller/controller.hpp" #include "karts/kart_properties.hpp" +#include "graphics/material.hpp" #include "physics/physics.hpp" #include "race/history.hpp" #include "states_screens/race_gui_base.hpp" +#include "tracks/graph_node.hpp" +#include "tracks/quad_graph.hpp" #include "tracks/track_sector.hpp" #include "tracks/track.hpp" #include "utils/constants.hpp" @@ -644,8 +647,8 @@ unsigned int LinearWorld::getRescuePositionIndex(AbstractKart *kart) // ------------------------------------------------------------------------ btTransform LinearWorld::getRescueTransform(unsigned int index) const { - const Vec3 &xyz = QuadGraph::get()->getQuadOfNode(index).getCenter(); - const Vec3 &normal = QuadGraph::get()->getQuadOfNode(index).getNormal(); + const Vec3 &xyz = QuadGraph::get()->getNode(index).getCenter(); + const Vec3 &normal = QuadGraph::get()->getNode(index).getNormal(); btTransform pos; pos.setOrigin(xyz); diff --git a/src/physics/physical_object.cpp b/src/physics/physical_object.cpp index 9e7abd0fc..7480563eb 100644 --- a/src/physics/physical_object.cpp +++ b/src/physics/physical_object.cpp @@ -24,6 +24,7 @@ using namespace irr; +#include "graphics/material.hpp" #include "graphics/material_manager.hpp" #include "graphics/mesh_tools.hpp" #include "io/file_manager.hpp" diff --git a/src/scriptengine/script_engine.hpp b/src/scriptengine/script_engine.hpp index e03ebaf62..7ea19f96c 100644 --- a/src/scriptengine/script_engine.hpp +++ b/src/scriptengine/script_engine.hpp @@ -22,8 +22,10 @@ #include #include #include +#include #include "scriptengine/script_utils.hpp" +#include "utils/no_copy.hpp" #include "utils/ptr_vector.hpp" class TrackObjectPresentation; diff --git a/src/tracks/battle_graph.cpp b/src/tracks/battle_graph.cpp index 14803b134..300b38e8c 100644 --- a/src/tracks/battle_graph.cpp +++ b/src/tracks/battle_graph.cpp @@ -1,3 +1,4 @@ + // // SuperTuxKart - a fun racing game with go-kart // Copyright (C) 2009-2015 Joerg Henrichs @@ -223,7 +224,7 @@ void BattleGraph::findItemsOnGraphNodes() for (unsigned int j = 0; j < this->getNumNodes(); ++j) { - if (getQuadOfNode(j).pointInQuad(xyz, false)) + if (getQuadOfNode(j).pointInside(xyz, false)) polygon = j; } @@ -248,7 +249,7 @@ int BattleGraph::pointToNode(const int cur_node, for (unsigned int node = 0; node < this->getNumNodes(); node++) { const Quad& quad = this->getQuadOfNode(node); - if (quad.pointInQuad(cur_point, ignore_vertical)) + if (quad.pointInside(cur_point, ignore_vertical)) { return node; } @@ -258,7 +259,7 @@ int BattleGraph::pointToNode(const int cur_node, { // Check if the point is still on the same node const Quad& cur_quad = this->getQuadOfNode(cur_node); - if (cur_quad.pointInQuad(cur_point, ignore_vertical)) return cur_node; + if (cur_quad.pointInside(cur_point, ignore_vertical)) return cur_node; // If not then check all nearby quads (8 quads) // Skip the same node @@ -267,7 +268,7 @@ int BattleGraph::pointToNode(const int cur_node, { const int test_node = m_nearby_quads[cur_node][i]; const Quad& quad = this->getQuadOfNode(test_node); - if (quad.pointInQuad(cur_point, ignore_vertical)) + if (quad.pointInside(cur_point, ignore_vertical)) { return test_node; } @@ -465,9 +466,3 @@ void BattleGraph::set3DVerticesOfGraph(int i, video::S3DVertex *v, { NavMesh::get()->getQuad(i).getVertices(v, color); } // set3DVerticesOfGraph - -// ---------------------------------------------------------------------------- -const bool BattleGraph::isNodeInvisible(int n) const -{ - return NavMesh::get()->getQuad(n).isInvisible(); -} // isNodeInvisible diff --git a/src/tracks/battle_graph.hpp b/src/tracks/battle_graph.hpp index b99fb607c..ed809fd22 100644 --- a/src/tracks/battle_graph.hpp +++ b/src/tracks/battle_graph.hpp @@ -76,7 +76,7 @@ private: virtual void getGraphBoundingBox(Vec3 *min, Vec3 *max) const { NavMesh::get()->getBoundingBox(min, max); } // ------------------------------------------------------------------------ - virtual const bool isNodeInvisible(int n) const; + virtual const bool isNodeInvisible(int n) const { return false; } // ------------------------------------------------------------------------ virtual const bool hasLapLine() const { return false; } diff --git a/src/tracks/check_manager.cpp b/src/tracks/check_manager.cpp index 264b40ce1..3f9b4f4fa 100644 --- a/src/tracks/check_manager.cpp +++ b/src/tracks/check_manager.cpp @@ -28,7 +28,8 @@ #include "tracks/check_lap.hpp" #include "tracks/check_line.hpp" #include "tracks/check_structure.hpp" -#include "tracks/track.hpp" +#include "tracks/quad_graph.hpp" +#include "utils/log.hpp" CheckManager *CheckManager::m_check_manager = NULL; diff --git a/src/tracks/graph_node.cpp b/src/tracks/graph_node.cpp index 0ff4cfaff..7e838ad5d 100644 --- a/src/tracks/graph_node.cpp +++ b/src/tracks/graph_node.cpp @@ -22,51 +22,44 @@ #include "io/xml_node.hpp" #include "matrix4.h" #include "tracks/quad_graph.hpp" -#include "tracks/quad_set.hpp" #include "utils/log.hpp" // ---------------------------------------------------------------------------- -/** Constructor. Saves the quad index which belongs to this graph node. - * \param index Index of the quad to use for this node (in QuadSet). - */ -GraphNode::GraphNode(unsigned int quad_index, unsigned int node_index) +GraphNode::GraphNode(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, + const Vec3 &p3, const Vec3 &normal, + unsigned int node_index, bool invisible, + bool ai_ignore) + :Quad(p0, p1, p2, p3) { - if (quad_index >= QuadSet::get()->getNumberOfQuads()) - Log::fatal("GraphNode", "No driveline found, or empty driveline."); - - m_quad_index = quad_index; + m_invisible = invisible; + m_ai_ignore = ai_ignore; + m_normal = normal; m_node_index = node_index; m_distance_from_start = -1.0f; - 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 (or the reverse if reverse mode is selected). // The width is the average width at the beginning and at the end. - m_right_unit_vector = ( quad[0]-quad[1] - +quad[3]-quad[2]) * 0.5f; + m_right_unit_vector = ( m_p[0]-m_p[1] + +m_p[3]-m_p[2]) * 0.5f; m_right_unit_vector.normalize(); - m_width = ( (quad[1]-quad[0]).length() - + (quad[3]-quad[2]).length() ) * 0.5f; + m_width = ( (m_p[1]-m_p[0]).length() + + (m_p[3]-m_p[2]).length() ) * 0.5f; if(QuadGraph::get()->isReverse()) { - m_lower_center = (quad[2]+quad[3]) * 0.5f; - m_upper_center = (quad[0]+quad[1]) * 0.5f; + m_lower_center = (m_p[2]+m_p[3]) * 0.5f; + m_upper_center = (m_p[0]+m_p[1]) * 0.5f; m_right_unit_vector *= -1.0f; } else { - m_lower_center = (quad[0]+quad[1]) * 0.5f; - m_upper_center = (quad[2]+quad[3]) * 0.5f; + m_lower_center = (m_p[0]+m_p[1]) * 0.5f; + m_upper_center = (m_p[2]+m_p[3]) * 0.5f; } - m_line = core::line3df(m_lower_center.toIrrVector(), - m_upper_center.toIrrVector()); - // Only this 2d point is needed later - m_lower_center_2d = core::vector2df(m_lower_center.getX(), - m_lower_center.getZ() ); - + } // GraphNode // ---------------------------------------------------------------------------- @@ -78,28 +71,25 @@ GraphNode::GraphNode(unsigned int quad_index, unsigned int node_index) void GraphNode::addSuccessor(unsigned int to) { m_successor_nodes.push_back(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); + GraphNode &gn_to = QuadGraph::get()->getNode(to); // Note that the first predecessor is (because of the way the quad graph // is exported) the most 'natural' one, i.e. the one on the main // driveline. - gn.m_predecessor_nodes.push_back(m_node_index); + gn_to.m_predecessor_nodes.push_back(m_node_index); - Vec3 d = m_lower_center - QuadGraph::get()->getNode(to).m_lower_center; + Vec3 d = m_lower_center - gn_to.m_lower_center; m_distance_to_next.push_back(d.length()); - - Vec3 diff = next_quad.getCenter() - this_quad.getCenter(); - + + Vec3 diff = gn_to.getCenter() - getCenter(); + core::CMatrix4 m; - m.buildRotateFromTo(this_quad.getNormal().toIrrVector(), + m.buildRotateFromTo(getNormal().toIrrVector(), Vec3(0, 1, 0).toIrrVector()); core::vector3df diffRotated; m.rotateVect(diffRotated, diff.toIrrVector()); - + m_angle_to_next.push_back(atan2(diffRotated.X, diffRotated.Z)); } // addSuccessor @@ -183,197 +173,17 @@ void GraphNode::setDirectionData(unsigned int successor, DirectionType dir, } // setDirectionData // ---------------------------------------------------------------------------- -/** Returns the distance a point has from this quad in forward and sidewards - * direction, i.e. how far forwards the point is from the beginning of the - * quad, and how far to the side from the line connecting the center points - * is it. - * \param xyz The coordinates of the point. - * \param result The X coordinate contains the sidewards distance, the - * Z coordinate the forward distance. - */ -void GraphNode::getDistances(const Vec3 &xyz, Vec3 *result) -{ - core::vector3df xyz_irr = xyz.toIrrVector(); - core::vector3df closest = m_line.getClosestPoint(xyz.toIrrVector()); - core::vector3df normal = getQuad().getNormal().toIrrVector(); - - if(xyz.sideofPlane(closest, closest+normal, m_line.end)<0) - result->setX( (closest-xyz_irr).getLength()); // to the right - else - result->setX(-(closest-xyz_irr).getLength()); // to the left - result->setZ( m_distance_from_start + - (closest-m_lower_center.toIrrVector()).getLength()); -} // getDistances - -// ---------------------------------------------------------------------------- -/** Returns the distance a point has from an unrolled quad in forward and sidewards - * direction, i.e. how far forwards the point is from the beginning of the - * quad, and how far to the side from the line connecting the center points - * is it. - * \param xyz The coordinates of the point. - * \param fork_number The fork on which the unrolled quad lies. - * \param quad_idx The index of the unrolled quad to find distances from. - * \param result The X coordinate contains the sidewards distance, the - * Z coordinate the forward distance. - */ -void GraphNode::getDistancesUnrolled(const Vec3 &xyz, const int fork_number, unsigned int quad_idx, Vec3 *result) -{ - - const Quad& unrolled = getUnrolledQuad(fork_number,quad_idx); - Vec3 upper_center, lower_center; - if (!QuadGraph::get()->isReverse()) - { - upper_center = 0.5f*(unrolled[2] + unrolled[3]), - lower_center = 0.5f*(unrolled[0] + unrolled[1]); - } - else - { - upper_center = 0.5f*(unrolled[0] + unrolled[1]), - lower_center = 0.5f*(unrolled[2] + unrolled[3]); - } - // center_line is from A to B, or lower_center to upper_center - core::vector3df A = lower_center.toIrrVector(), B = upper_center.toIrrVector(); - result->setX(((B - A).crossProduct(xyz.toIrrVector() - A)).getLength() / (B - A).getLength()); - - result->setZ(QuadGraph::get()->getNode((m_node_index + quad_idx)%QuadGraph::get()->getNumNodes()).getDistanceFromStart() - + (xyz - lower_center).length()); -} // getDistancesUnrolled - -// ---------------------------------------------------------------------------- -/** Returns the square of the distance between the given point and any point - * on the 'centre' line, i.e. the finite line from the middle point of the - * lower end of the quad node to the middle point of the upper end of the - * quad which belongs to this graph node. The value is computed in 2d only! - * \param xyz The point for which the distance to the line is computed. - */ -float GraphNode::getDistance2FromPoint(const Vec3 &xyz) -{ - core::vector3df closest = m_line.getClosestPoint(xyz.toIrrVector()); - return (closest-xyz.toIrrVector()).getLengthSQ(); -} // getDistance2FromPoint - -// ---------------------------------------------------------------------------- - void GraphNode::setChecklineRequirements(int latest_checkline) { m_checkline_requirements.push_back(latest_checkline); -} - - -const Vec3 GraphNode::getPointTransformedToFlatQuad(Vec3 xyz) -{ - Quad thisQuad = getQuad(); - core::CMatrix4 m; - m.buildRotateFromTo(thisQuad.getNormal().toIrrVector(), core::vector3df(0, 1, 0)); - - core::vector3df result; - // Translate the input point into the Quads frame of reference, then rotate - m.rotateVect(result, (xyz - thisQuad.getCenter()).toIrrVector()); - - return (Vec3)result; -} +} // setChecklineRequirements // ---------------------------------------------------------------------------- -/** This functions builds unrolled quads for this node. This takes into account - * if there is a fork in coming up. In this case there will be two sets of - * unrolled quads for this node. One for each fork. - * \param unroll_quad_count The length of the unrolled set of quads per node. +/** Returns true if the index-successor of this node is one that the AI + * is allowed to use. + * \param index Index of the successor. */ -void GraphNode::buildUnrolledQuads(unsigned int unroll_quad_count) +bool GraphNode::ignoreSuccessorForAI(unsigned int i) const { - m_unrolled_quads.clear(); - - unsigned int numberOfForks = 1; - GraphNode* currentNode = this; - for (unsigned i = 0; i < unroll_quad_count+1; i++) - { - unsigned int successorCount = currentNode->getNumberOfSuccessors(); - if (successorCount >= 2) - { - numberOfForks = successorCount; - break; - } - else - { - currentNode = &QuadGraph::get()->getNode(currentNode->getSuccessor(0)); - - } - } - - m_unrolled_quads.resize(numberOfForks); - - for (unsigned i = 0; i < numberOfForks; i++) - { - Quad thisQuad = getQuad(); - m_unrolled_quads[i].push_back(thisQuad); - GraphNode& next = QuadGraph::get()->getNode(getSuccessor(i%getNumberOfSuccessors())); - addUnrolledQuad(next , i , unroll_quad_count); - } -} // buildUnrolledQuads - -// ---------------------------------------------------------------------------- -/** This function is called recursively to build one set of unrolled quads. Each - * call adds one unrolled quad to the existing set. - * \param unroll_quad_count The length of the unrolled set of quads per node. - */ -void GraphNode::addUnrolledQuad(const GraphNode& next_node, int fork_number, int k) -{ - if (k == 0) return; - - Quad next_quad = next_node.getQuad(); - Quad next_quad_to_push = next_quad.getFlattenedQuad(); - Quad last_pushed_quad = m_unrolled_quads[fork_number].back(); - - - core::CMatrix4 m; - core::vector3df new_points[4]; - - // First rotate the next_quad_to_push so that it aligns with last quad - // in the vector of unrolled quads - m.buildRotateFromTo(next_quad_to_push.getNormal().toIrrVector(), - last_pushed_quad.getNormal().toIrrVector()); - - for (unsigned int i = 0; i < 4; i++) - m.rotateVect(new_points[i], next_quad_to_push[i].toIrrVector()); - - Vec3 endEdge, beginEdge; - if (!QuadGraph::get()->isReverse()) - { - endEdge = (last_pushed_quad[2] - last_pushed_quad[3]); - beginEdge = (new_points[1] - new_points[0]); - } - else - { - endEdge = (last_pushed_quad[1] - last_pushed_quad[0]); - beginEdge = (new_points[2] - new_points[3]); - } - - m.buildRotateFromTo(beginEdge.toIrrVector(), endEdge.toIrrVector()); - for (unsigned int i = 0; i < 4; i++) - m.rotateVect(new_points[i]); - - // Next translate the new quad to be pushed to the correct position infront - // of the last quad in the vector of unrolled quads - Vec3 lower_center, upper_center; - if (!QuadGraph::get()->isReverse()) - { - lower_center = 0.5f*(new_points[0] + new_points[1]); - upper_center = 0.5f*(last_pushed_quad[2] + last_pushed_quad[3]); - } - else - { - lower_center = 0.5f*(new_points[2] + new_points[3]); - upper_center = 0.5f*(last_pushed_quad[0] + last_pushed_quad[1]); - } - m.setTranslation((upper_center-lower_center).toIrrVector()); - - for (unsigned int i = 0; i < 4; i++) - m.translateVect(new_points[i]); - - // Push the quad into the vector of unrolled quads - m_unrolled_quads[fork_number].push_back(Quad(new_points[0], new_points[1], new_points[2], new_points[3])); - k = k - 1; - // Recurisvely build the vector of unrolled quads till k reduces to 0 - GraphNode& next = QuadGraph::get()->getNode(next_node.getSuccessor(fork_number%next_node.getNumberOfSuccessors())); - addUnrolledQuad(next, fork_number, k); -} // addUnrolledQuad + return QuadGraph::get()->getNode(m_successor_nodes[i]).letAIIgnore(); +} // ignoreSuccessorForAI diff --git a/src/tracks/graph_node.hpp b/src/tracks/graph_node.hpp index 10666b7f5..c8850540e 100644 --- a/src/tracks/graph_node.hpp +++ b/src/tracks/graph_node.hpp @@ -21,22 +21,14 @@ #include -#include -#include -#include - #include "tracks/quad.hpp" -#include "tracks/quad_set.hpp" -#include "utils/vec3.hpp" - -class QuadGraph; /** * \brief This class stores a node of the graph, i.e. a list of successor - * edges. + * edges, it can either be 2d or 3d. * \ingroup tracks */ -class GraphNode +class GraphNode : public Quad { public: /** To indiciate in which direction the track is going: @@ -44,12 +36,25 @@ public: * AI only. */ enum DirectionType {DIR_STRAIGHT, DIR_LEFT, DIR_RIGHT, DIR_UNDEFINED}; +protected: + /** Lower center point of the graph node. */ + Vec3 m_lower_center; + + /** Upper center point of the graph node. */ + Vec3 m_upper_center; + + /** Distance from the start to the beginning of the graph node. */ + float m_distance_from_start; private: - /** Index of this node in the set of quads. Several graph nodes can use - * the same quad, meaning it is possible to use a quad more than once, - * e.g. a figure 8 like track. */ - unsigned int m_quad_index; + /** Normal of the graph node */ + Vec3 m_normal; + + /** Set to true if this graph node should not be shown in the minimap. */ + bool m_invisible; + + /** Set to true if this graph node should not be used by the AI. */ + bool m_ai_ignore; /** Index of this graph node. */ unsigned int m_node_index; @@ -66,83 +71,59 @@ private: /** The angle of the line from this node to each neighbour. */ std::vector m_angle_to_next; - /** Distance from the start to the beginning of this quad. */ - float m_distance_from_start; - /** Width of the track, which is the average of the width at the - * beginning and at the end. FIXME: for now the width is independent - * of the orientation (e.g. a quad used more than once might once - * be used from top to bottom, one from left to right, so it should - * have a different width then). */ - float m_width; + * beginning and at the end. */ + float m_width; - /** The center point of the lower two points (e.g. points 0 and 1). - * This saves some computations in getDistances later. Only the - * start point is needed, and only in 2d. */ - core::vector2df m_lower_center_2d; + /** A vector from the center of the quad to the right edge. */ + Vec3 m_center_to_right; - /** Lower center point of the graph node. */ - Vec3 m_lower_center; + typedef std::vector PathToNodeVector; + /** This vector is only used if the graph node has more than one + * successor. In this case m_path_to_node[X] will contain the index + * of the successor to use in order to reach graph node X for this + * graph nodes. */ + PathToNodeVector m_path_to_node; - /** Upper center point of the graph node. */ - Vec3 m_upper_center; + /** The direction for each of the successors. */ + std::vector m_direction; - /** A vector from the center of the quad to the right edge. */ - Vec3 m_center_to_right; + /** Stores for each successor the index of the last graph node that + * has the same direction (i.e. if index 0 curves left, this vector + * will store the index of the last graph node that is still turning + * left. */ + std::vector m_last_index_same_direction; - /** Line between lower and upper center, saves computation in - * getDistanceFromLine() later. The line is 2d only since otherwise - * taller karts would have a larger distance from the center. It also - * saves computation, and it is only needed to determine the distance - * from the center of the drivelines anyway. */ - core::line3df m_line; - - typedef std::vector PathToNodeVector; - /** This vector is only used if the graph node has more than one - * successor. In this case m_path_to_node[X] will contain the index - * of the successor to use in order to reach graph node X for this - * graph nodes. */ - PathToNodeVector m_path_to_node; - - /** The direction for each of the successors. */ - std::vector m_direction; - - /** Stores for each successor the index of the last graph node that - * has the same direction (i.e. if index 0 curves left, this vector - * will store the index of the last graph node that is still turning - * left. */ - std::vector m_last_index_same_direction; - - /** A unit vector pointing from the center to the right side, orthogonal - * to the driving direction. */ - Vec3 m_right_unit_vector; + /** A unit vector pointing from the center to the right side, orthogonal + * to the driving direction. */ + Vec3 m_right_unit_vector; /** - * Sets of checklines you should have activated when you are driving on - * this node (there is a possibility of more than one set because of - * alternate ways) - */ - std::vector< int > m_checkline_requirements; + * Sets of checklines you should have activated when you are driving on + * this node (there is a possibility of more than one set because of + * alternate ways) + */ + std::vector< int > m_checkline_requirements; - std::vector< std::vector >m_unrolled_quads; - - void markAllSuccessorsToUse(unsigned int n, + void markAllSuccessorsToUse(unsigned int n, PathToNodeVector *m_path_to_node); - void addUnrolledQuad(const GraphNode& next_node, int fork_number, int k); - public: - GraphNode(unsigned int quad_index, unsigned int node_index); + GraphNode(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, + const Vec3 &p3, const Vec3 &normal, + unsigned int node_index, bool invisible, + bool ai_ignore); + // ------------------------------------------------------------------------ + virtual ~GraphNode() {} + // ------------------------------------------------------------------------ void addSuccessor (unsigned int to); - void getDistances(const Vec3 &xyz, Vec3 *result); - void getDistancesUnrolled(const Vec3 &xyz, const int fork_number, - unsigned int quad_idx, Vec3 *result); - float getDistance2FromPoint(const Vec3 &xyz); + // ------------------------------------------------------------------------ void setupPathsToNode(); + // ------------------------------------------------------------------------ void setChecklineRequirements(int latest_checkline); + // ------------------------------------------------------------------------ void setDirectionData(unsigned int successor, DirectionType dir, unsigned int last_node_index); - void buildUnrolledQuads(unsigned int unroll_quad_count); // ------------------------------------------------------------------------ /** Returns the number of successors. */ unsigned int getNumberOfSuccessors() const @@ -161,23 +142,15 @@ public: */ int getPredecessor(unsigned int i) const {return m_predecessor_nodes[i]; } // ------------------------------------------------------------------------ - /** Returns the quad_index in the quad_set of this node. */ - int getQuadIndex() const { return m_quad_index; } + /** Returns the node index of this node. */ + unsigned int getNodeIndex() const { return m_node_index; } // ------------------------------------------------------------------------ - /** Returns the quad of this graph node. */ - 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 QuadSet::get()->getQuad(m_quad_index)[i];} + /** Returns the normal of this quad */ + const Vec3& getNormal() const { return m_normal; } // ------------------------------------------------------------------------ /** Returns the distance to the j-th. successor. */ float getDistanceToSuccessor(unsigned int j) const { return m_distance_to_next[j]; } - // ------------------------------------------------------------------------ /** Returns the angle from this node to the j-th. successor. */ float getAngleToSuccessor(unsigned int j) const @@ -199,21 +172,11 @@ public: /** Returns the center point of the upper edge of this graph node. */ const Vec3& getUpperCenter() const {return m_upper_center;} // ------------------------------------------------------------------------ - /** Returns the center point of this graph node. */ - const Vec3 getCenter() const - {return (m_upper_center + m_lower_center) / 2.0f;} - // ------------------------------------------------------------------------ /** Returns the length of the quad of this node. */ float getNodeLength() const {return (m_lower_center-m_upper_center).length();} // ------------------------------------------------------------------------ - /** Returns true if the index-successor of this node is one that the AI - * is allowed to use. - * \param index Index of the successor. */ - bool ignoreSuccessorForAI(unsigned int i) const - { - return QuadSet::get()->getQuad(m_successor_nodes[i]).letAIIgnore(); - }; + bool ignoreSuccessorForAI(unsigned int i) const; // ------------------------------------------------------------------------ /** Returns which successor node to use in order to be able to reach the * given node n. @@ -239,10 +202,18 @@ public: // ------------------------------------------------------------------------ /** Returns a unit vector pointing to the right side of the quad. */ const Vec3 &getRightUnitVector() const { return m_right_unit_vector; } + // ------------------------------------------------------------------------ + /** Returns true of this node is invisible, i.e. not to be shown in + * the minimap. */ + bool isInvisible() const { return m_invisible; } + // ------------------------------------------------------------------------ + /** True if this node should be ignored by the AI. */ + bool letAIIgnore() const { return m_ai_ignore; } + // ------------------------------------------------------------------------ + virtual float getDistance2FromPoint(const Vec3 &xyz) = 0; + // ------------------------------------------------------------------------ + virtual void getDistances(const Vec3 &xyz, Vec3 *result) = 0; - const Vec3 getPointTransformedToFlatQuad(Vec3 xyz); - - const Quad& getUnrolledQuad(int succ_idx, int i) const { return m_unrolled_quads[succ_idx][i]; } }; // GraphNode #endif diff --git a/src/tracks/navmesh.cpp b/src/tracks/navmesh.cpp index 74d525add..1da2bc574 100644 --- a/src/tracks/navmesh.cpp +++ b/src/tracks/navmesh.cpp @@ -95,6 +95,14 @@ NavMesh::NavMesh(const std::string &filename) m_quads.push_back(new Quad( all_vertices[quad_index[0]], all_vertices[quad_index[1]], all_vertices[quad_index[2]], all_vertices[quad_index[3]])); + +/*(AbstractNode* tn = createNode( + all_vertices[quad_index[0]], all_vertices[quad_index[1]], + all_vertices[quad_index[2]], all_vertices[quad_index[3]],false,false); + +Vec3 pp(0,0,0); +tn->pointInNode(pp);*/ + } } } diff --git a/src/tracks/node_2d.cpp b/src/tracks/node_2d.cpp new file mode 100644 index 000000000..ad6dcba61 --- /dev/null +++ b/src/tracks/node_2d.cpp @@ -0,0 +1,69 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "tracks/node_2d.hpp" + +// ---------------------------------------------------------------------------- +Node2D::Node2D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, + const Vec3 &normal, unsigned int node_index, bool invisible, + bool ai_ignore) + : GraphNode(p0, p1, p2, p3, normal, node_index, invisible, ai_ignore) +{ + m_line = core::line2df(m_upper_center.getX(), m_upper_center.getZ(), + m_lower_center.getX(), m_lower_center.getZ()); + + // Only this 2d point is needed later + m_lower_center_2d = core::vector2df(m_lower_center.getX(), + m_lower_center.getZ()); +} // Node2D + +// ---------------------------------------------------------------------------- +/** Returns the distance a point has from this node in forward and sidewards + * direction, i.e. how far forwards the point is from the beginning of the + * node, and how far to the side from the line connecting the center points + * is it. All these computations are done in 2D only. + * \param xyz The coordinates of the point. + * \param result The X coordinate contains the sidewards distance, the + * Z coordinate the forward distance. + */ +void Node2D::getDistances(const Vec3 &xyz, Vec3 *result) +{ + core::vector2df xyz2d(xyz.getX(), xyz.getZ()); + core::vector2df closest = m_line.getClosestPoint(xyz2d); + if (m_line.getPointOrientation(xyz2d) > 0) + 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()); +} // getDistances + +// ---------------------------------------------------------------------------- +/** Returns the square of the distance between the given point and any point + * on the 'centre' line, i.e. the finite line from the middle point of the + * lower end of the node to the middle point of the upper end of the node + * which belongs to this graph node. The value is computed in 2d only! + * \param xyz The point for which the distance to the line is computed. + */ +float Node2D::getDistance2FromPoint(const Vec3 &xyz) +{ + core::vector2df xyz2d(xyz.getX(), xyz.getZ()); + core::vector2df closest = m_line.getClosestPoint(xyz2d); + return (closest-xyz2d).getLengthSQ(); +} // getDistance2FromPoint diff --git a/src/tracks/node_2d.hpp b/src/tracks/node_2d.hpp new file mode 100644 index 000000000..cb6bb3874 --- /dev/null +++ b/src/tracks/node_2d.hpp @@ -0,0 +1,55 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_NODE_2D_HPP +#define HEADER_NODE_2D_HPP + +#include "tracks/graph_node.hpp" +#include "utils/cpp2011.hpp" + +#include + +/** + * \ingroup tracks + */ +class Node2D : public GraphNode +{ +private: + /** The center point of the lower two points (e.g. points 0 and 1). + * This saves some computations in getDistances later. Only the + * start point is needed, and only in 2d. */ + core::vector2df m_lower_center_2d; + + /** Line between lower and upper center, saves computation in + * getDistance() later. The line is 2d only since otherwise taller karts + * would have a larger distance from the center. It also saves + * computation, and it is only needed to determine the distance from the + * center of the drivelines anyway. */ + core::line2df m_line; + +public: + Node2D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, + const Vec3 &normal, unsigned int node_index, bool invisible, + bool ai_ignore); + // ------------------------------------------------------------------------ + virtual void getDistances(const Vec3 &xyz, Vec3 *result) OVERRIDE; + // ------------------------------------------------------------------------ + virtual float getDistance2FromPoint(const Vec3 &xyz) OVERRIDE; + +}; +#endif diff --git a/src/tracks/node_3d.cpp b/src/tracks/node_3d.cpp new file mode 100644 index 000000000..9593e5666 --- /dev/null +++ b/src/tracks/node_3d.cpp @@ -0,0 +1,94 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "tracks/node_3d.hpp" + +// ---------------------------------------------------------------------------- +Node3D::Node3D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, + const Vec3 &normal, unsigned int node_index, bool invisible, + bool ai_ignore) + : GraphNode(p0, p1, p2, p3, normal, node_index, invisible, ai_ignore) +{ + // Compute the node bounding box used for pointInNode + Vec3 box_corners[8]; + float box_high = 5.0f; + float box_low = 1.0f; + box_corners[0] = m_p[0] + box_high * normal; + box_corners[1] = m_p[1] + box_high * normal; + box_corners[2] = m_p[2] + box_high * normal; + box_corners[3] = m_p[3] + box_high * normal; + box_corners[4] = m_p[0] - box_low * normal; + box_corners[5] = m_p[1] - box_low * normal; + box_corners[6] = m_p[2] - box_low * normal; + box_corners[7] = m_p[3] - box_low * normal; + + Vec3 box_faces[6][4] = + { + { box_corners[0], box_corners[1], box_corners[2], box_corners[3] }, + { box_corners[3], box_corners[2], box_corners[6], box_corners[7] }, + { box_corners[7], box_corners[6], box_corners[5], box_corners[4] }, + { box_corners[1], box_corners[0], box_corners[4], box_corners[5] }, + { box_corners[4], box_corners[0], box_corners[3], box_corners[7] }, + { box_corners[1], box_corners[5], box_corners[6], box_corners[2] } + }; + + for (unsigned int i = 0; i < 6 ; i++) + { + for (unsigned int j = 0; j < 4; j++) + m_box_faces[i][j] = box_faces[i][j]; + } + + m_line = core::line3df(m_lower_center.toIrrVector(), + m_upper_center.toIrrVector()); +} // Node3D + +// ---------------------------------------------------------------------------- +/** Returns the distance a point has from this node in forward and sidewards + * direction, i.e. how far forwards the point is from the beginning of the + * node, and how far to the side from the line connecting the center points + * is it. + * \param xyz The coordinates of the point. + * \param result The X coordinate contains the sidewards distance, the + * Z coordinate the forward distance. + */ +void Node3D::getDistances(const Vec3 &xyz, Vec3 *result) +{ + core::vector3df xyz_irr = xyz.toIrrVector(); + core::vector3df closest = m_line.getClosestPoint(xyz.toIrrVector()); + core::vector3df normal = getNormal().toIrrVector(); + + if (xyz.sideofPlane(closest, closest + normal, m_line.end) < 0) + result->setX( (closest-xyz_irr).getLength()); // to the right + else + result->setX(-(closest-xyz_irr).getLength()); // to the left + result->setZ(m_distance_from_start + + (closest-m_lower_center.toIrrVector()).getLength()); +} // getDistances + +// ---------------------------------------------------------------------------- +/** Returns the square of the distance between the given point and any point + * on the 'centre' line, i.e. the finite line from the middle point of the + * lower end of the node to the middle point of the upper end of the node + * which belongs to this node. + * \param xyz The point for which the distance to the line is computed. + */ +float Node3D::getDistance2FromPoint(const Vec3 &xyz) +{ + core::vector3df closest = m_line.getClosestPoint(xyz.toIrrVector()); + return (closest-xyz.toIrrVector()).getLengthSQ(); +} // getDistance2FromPoint diff --git a/src/tracks/node_3d.hpp b/src/tracks/node_3d.hpp new file mode 100644 index 000000000..bd395fabd --- /dev/null +++ b/src/tracks/node_3d.hpp @@ -0,0 +1,64 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_NODE_3D_HPP +#define HEADER_NODE_3D_HPP + +#include "tracks/graph_node.hpp" +#include "utils/cpp2011.hpp" + +/** + * \ingroup tracks + */ +class Node3D : public GraphNode +{ +private: + /** For each node, construct a 3D box to check if a point lies inside it. + */ + Vec3 m_box_faces[6][4]; + + /** Line between lower and upper center, saves computation in + * getDistance() later. + */ + core::line3df m_line; + +public: + Node3D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, + const Vec3 &normal, unsigned int node_index, bool invisible, + bool ai_ignore); + // ------------------------------------------------------------------------ + virtual bool pointInside(const Vec3& p, + bool ignore_vertical = false) const OVERRIDE + { + float side = p.sideofPlane(m_box_faces[0][0], m_box_faces[0][1], + m_box_faces[0][2]); + for (int i = 1; i < 6; i++) + { + if (side * p.sideofPlane(m_box_faces[i][0], m_box_faces[i][1], + m_box_faces[i][2]) < 0) + return false; + } + return true; + } + // ------------------------------------------------------------------------ + virtual void getDistances(const Vec3 &xyz, Vec3 *result) OVERRIDE; + // ------------------------------------------------------------------------ + virtual float getDistance2FromPoint(const Vec3 &xyz) OVERRIDE; + +}; +#endif diff --git a/src/tracks/quad.cpp b/src/tracks/quad.cpp index 44763f397..d6a697ec7 100644 --- a/src/tracks/quad.cpp +++ b/src/tracks/quad.cpp @@ -20,56 +20,21 @@ #include "utils/log.hpp" #include -#include #include #include #include "LinearMath/btTransform.h" /** Constructor, takes 4 points. */ -Quad::Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, - bool invisible, bool ai_ignore) - { - +Quad::Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3) +{ m_p[0]=p0; m_p[1]=p1; m_p[2]=p2; m_p[3]=p3; - + m_center = 0.25f*(p0+p1+p2+p3); m_min_height = std::min ( std::min(p0.getY(), p1.getY()), std::min(p2.getY(), p3.getY()) ); m_max_height = std::max ( std::max(p0.getY(), p1.getY()), std::max(p2.getY(), p3.getY()) ); - m_invisible = invisible; - m_ai_ignore = ai_ignore; - - findNormal(); - - // Compute the quad bounding box used for pointInQuad - Vec3 boxCorners[8]; - Vec3 normal = getNormal(); - float boxHigh = 5.0f; - float boxLow = 1.0f; - boxCorners[0] = m_p[0] + boxHigh*normal; - boxCorners[1] = m_p[1] + boxHigh*normal; - boxCorners[2] = m_p[2] + boxHigh*normal; - boxCorners[3] = m_p[3] + boxHigh*normal; - boxCorners[4] = m_p[0] - boxLow*normal; - boxCorners[5] = m_p[1] - boxLow*normal; - boxCorners[6] = m_p[2] - boxLow*normal; - boxCorners[7] = m_p[3] - boxLow*normal; - - Vec3 boxFaces[6][4] = { - { boxCorners[0], boxCorners[1], boxCorners[2], boxCorners[3] }, - { boxCorners[3], boxCorners[2], boxCorners[6], boxCorners[7] }, - { boxCorners[7], boxCorners[6], boxCorners[5], boxCorners[4] }, - { boxCorners[1], boxCorners[0], boxCorners[4], boxCorners[5] }, - { boxCorners[4], boxCorners[0], boxCorners[3], boxCorners[7] }, - { boxCorners[1], boxCorners[5], boxCorners[6], boxCorners[2] } - }; - - for (unsigned int i = 0; i < 6 ; i++) - for (unsigned int j = 0; j < 4; j++) - m_box_faces[i][j] = boxFaces[i][j]; - } // Quad // ---------------------------------------------------------------------------- @@ -108,7 +73,7 @@ void Quad::getVertices(video::S3DVertex *v, const video::SColor &color) const } // setVertices // ---------------------------------------------------------------------------- -bool Quad::pointInQuad(const Vec3& p, bool ignore_vertical) const +bool Quad::pointInside(const Vec3& p, bool ignore_vertical) const { // In case that a kart can validly run too high over one driveline // and it should not be considered to be on that driveline. Example: @@ -136,20 +101,7 @@ bool Quad::pointInQuad(const Vec3& p, bool ignore_vertical) const return p.sideOfLine2D(m_p[2], m_p[3]) > 0.0 && p.sideOfLine2D(m_p[3], m_p[0]) >= 0.0; } -} // pointInQuad - -// ---------------------------------------------------------------------------- -/** Checks if a given point p lies in the bounding box of this quad */ -bool Quad::pointInQuad3D(const Vec3& p) const -{ - float side = p.sideofPlane(m_box_faces[0][0], m_box_faces[0][1], m_box_faces[0][2]); - for (int i = 1; i < 6; i++) - { - if (side*p.sideofPlane(m_box_faces[i][0], m_box_faces[i][1], m_box_faces[i][2]) < 0) return false; - } - return true; -} // pointInQuad3D - +} // pointInside // ---------------------------------------------------------------------------- /** Transforms a quad by a given transform (i.e. translation+rotation). This @@ -171,36 +123,3 @@ void Quad::transform(const btTransform &t, Quad *result) const std::min(result->m_p[2].getY(), result->m_p[3].getY()) ); } // transform - -// ---------------------------------------------------------------------------- -/** Find the normal of this quad by computing the normal of two triangles and taking - * their average. - */ -void Quad::findNormal() -{ - core::triangle3df tri1(m_p[0].toIrrVector(), m_p[1].toIrrVector(), m_p[2].toIrrVector()); - core::triangle3df tri2(m_p[0].toIrrVector(), m_p[2].toIrrVector(), m_p[3].toIrrVector()); - Vec3 normal1 = tri1.getNormal(); - Vec3 normal2 = tri2.getNormal(); - m_normal = -0.5f*(normal1 + normal2); - m_normal.normalize(); - -} // findNormal - -// ---------------------------------------------------------------------------- -/** Return a flattened version of this quad. */ -Quad Quad::getFlattenedQuad() -{ - core::CMatrix4 m; - m.buildRotateFromTo(m_normal.toIrrVector(), core::vector3df(0, 1, 0)); - Vec3 m_p_flat[4]; - for (unsigned int i = 0; i < 4; i++) - { - m.rotateVect(m_p_flat[i], (m_p[i] - m_center).toIrrVector()); - - m_p_flat[i].setY(0); - } - - return Quad(m_p_flat[0], m_p_flat[1], m_p_flat[2], m_p_flat[3]); - -} // getFlattenedQuad diff --git a/src/tracks/quad.hpp b/src/tracks/quad.hpp index 4ca8d8033..cd164fb7c 100644 --- a/src/tracks/quad.hpp +++ b/src/tracks/quad.hpp @@ -20,9 +20,10 @@ #define HEADER_QUAD_HPP #include -#include "utils/vec3.hpp" -#include +#include "utils/leak_check.hpp" +#include "utils/no_copy.hpp" +#include "utils/vec3.hpp" namespace irr { @@ -35,9 +36,9 @@ class btTransform; /** * \ingroup tracks */ -class Quad +class Quad : public NoCopy { -private: +protected: /** The four points of a quad. */ Vec3 m_p[4]; @@ -45,8 +46,7 @@ private: * This saves some computations at runtime. */ Vec3 m_center; - Vec3 m_normal; - +private: /** The minimum height of the quad, used in case that several quads * are on top of each other when determining the sector a kart is on. */ float m_min_height; @@ -55,47 +55,28 @@ private: * to distinguish between quads which are on top of each other. */ float m_max_height; - /** Set to true if this quad should not be shown in the minimap. */ - bool m_invisible; - - /** Set if this quad should not be used by the AI. */ - bool m_ai_ignore; - - /** For each quad, construct a 3D box to check if a point lies inside it. */ - Vec3 m_box_faces[6][4]; - - /** Find the normal of this quad */ - void findNormal(); - public: - Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, - bool invis=false, bool ai_ignore=false); + LEAK_CHECK() + // ------------------------------------------------------------------------ + Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3); + // ------------------------------------------------------------------------ + virtual ~Quad() {} + // ------------------------------------------------------------------------ void getVertices(video::S3DVertex *v, const video::SColor &color) const; - bool pointInQuad(const Vec3& p, bool ignore_vertical = false) const; - bool pointInQuad3D(const Vec3& p) const; + // ------------------------------------------------------------------------ void transform(const btTransform &t, Quad *result) const; // ------------------------------------------------------------------------ /** Returns the i-th. point of a quad. */ - const Vec3& operator[](int i) const {return m_p[i]; } + const Vec3& operator[](int i) const { return m_p[i]; } // ------------------------------------------------------------------------ /** Returns the center of a quad. */ - const Vec3& getCenter () const {return m_center; } + const Vec3& getCenter () const { return m_center; } // ------------------------------------------------------------------------ /** Returns the minimum height of a quad. */ - float getMinHeight() const { return m_min_height; } + float getMinHeight() const { return m_min_height; } // ------------------------------------------------------------------------ - /** Returns true of this quad is invisible, i.e. not to be shown in - * the minimap. */ - bool isInvisible() const { return m_invisible; } - // ------------------------------------------------------------------------ - /** True if this quad should be ignored by the AI. */ - bool letAIIgnore() const { return m_ai_ignore; } - // ------------------------------------------------------------------------ - /** Returns the normal of this quad */ - const Vec3& getNormal() const { return m_normal; } - // ------------------------------------------------------------------------ - /** Return a flattened version of this quad */ - Quad getFlattenedQuad(); + virtual bool pointInside(const Vec3& p, + bool ignore_vertical = false) const; }; // class Quad #endif diff --git a/src/tracks/quad_graph.cpp b/src/tracks/quad_graph.cpp index 6fcfac3fc..00cdb3f72 100644 --- a/src/tracks/quad_graph.cpp +++ b/src/tracks/quad_graph.cpp @@ -35,13 +35,47 @@ #include "tracks/check_lap.hpp" #include "tracks/check_line.hpp" #include "tracks/check_manager.hpp" -#include "tracks/quad_set.hpp" +#include "tracks/node_2d.hpp" +#include "tracks/node_3d.hpp" #include "tracks/track.hpp" #include "graphics/glwrap.hpp" const int QuadGraph::UNKNOWN_SECTOR = -1; QuadGraph *QuadGraph::m_quad_graph = NULL; +/** Factory method to dynamic create 2d / 3d node */ +GraphNode* createNode(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, + const Vec3 &p3, unsigned int node_index, + bool invisible, bool ai_ignore) +{ + // Find the normal of this node by computing the normal of two triangles + // and taking their average. + core::triangle3df tri1(p0.toIrrVector(), p1.toIrrVector(), + p2.toIrrVector()); + core::triangle3df tri2(p0.toIrrVector(), p2.toIrrVector(), + p3.toIrrVector()); + Vec3 normal1 = tri1.getNormal(); + Vec3 normal2 = tri2.getNormal(); + Vec3 normal = -0.5f * (normal1 + normal2); + normal.normalize(); + + // Use the angle between the normal and an up vector to choose 3d/2d node + const float angle = normal.angle(Vec3(0, 1, 0)); + if (angle > 0.5f) + { + Log::debug("TrackNode", "3d node created, normal: %f, %f, %f", + normal.x(), normal.y(), normal.z()); + return new Node3D(p0, p1, p2, p3, normal, node_index, invisible, + ai_ignore); + } + + Log::debug("TrackNode", "2d node created, normal: %f, %f, %f", + normal.x(), normal.y(), normal.z()); + return new Node2D(p0, p1, p2, p3, normal, node_index, invisible, + ai_ignore); +} // createNode + +// ---------------------------------------------------------------------------- /** Constructor, loads the graph information for a given set of quads * from a graph file. * \param quad_file_name Name of the file of all quads @@ -52,20 +86,17 @@ QuadGraph::QuadGraph(const std::string &quad_file_name, const bool reverse) : m_reverse(reverse) { m_lap_length = 0; - m_unroll_quad_count = 6; - QuadSet::create(); - QuadSet::get()->init(quad_file_name); m_quad_filename = quad_file_name; m_quad_graph = this; - load(graph_file_name); + load(quad_file_name, graph_file_name); } // QuadGraph -// ----------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- /** Destructor, removes all nodes of the graph. */ QuadGraph::~QuadGraph() { - QuadSet::destroy(); - for(unsigned int i=0; iaddSuccessor(from); else m_all_nodes[from]->addSuccessor(to); + } // addSuccessor -// ----------------------------------------------------------------------------- -/** Loads a quad graph from a file. - * \param filename Name of the file to load. - */ -void QuadGraph::load(const std::string &filename) +// ---------------------------------------------------------------------------- +/** This function interprets a point specification as an attribute in the + xml quad file. It understands two different specifications: + p1="n:p" : get point p from square n (n, p integers) + p1="p1,p2,p3" : make a 3d point out of these 3 floating point values +*/ +void QuadGraph::getPoint(const XMLNode *xml, const std::string &attribute_name, + Vec3* result) const { + std::string s; + xml->get(attribute_name, &s); + int pos=(int)s.find_first_of(":"); + if(pos>0) // n:p specification + { + std::vector l = StringUtils::split(s, ':'); + int n=atoi(l[0].c_str()); + int p=atoi(l[1].c_str()); + *result=(*m_all_nodes[n])[p]; + } + else + { + xml->get(attribute_name, result); + } + +} // getPoint + +// ---------------------------------------------------------------------------- +/** Loads a quad graph from a file. + * \param filename Name of the quad file to load. + * \param filename Name of the graph file to load. + */ +void QuadGraph::load(const std::string &quad_file_name, + const std::string &filename) +{ + XMLNode *quad = file_manager->createXMLTree(quad_file_name); + if (!quad || quad->getName() != "quads") + { + Log::error("Quad Graph : Quad xml '%s' not found.", filename.c_str()); + delete quad; + return; + } + + m_min = Vec3( 99999, 99999, 99999); + m_max = Vec3(-99999, -99999, -99999); + // Each quad is part of the graph exactly once now. + for(unsigned int i=0; igetNumNodes(); i++) + { + const XMLNode *xml_node = quad->getNode(i); + if(xml_node->getName()!="quad") + { + Log::warn("Quad Graph: Unsupported node type '%s' found in '%s' - ignored.", + xml_node->getName().c_str(), filename.c_str()); + continue; + } + + // Note that it's not easy to do the reading of the parameters here + // in quad, since the specification in the xml can contain references + // to previous points. E.g.: + // get("invisible", &invisible); + bool ai_ignore=false; + xml_node->get("ai-ignore", &ai_ignore); + GraphNode* node = + createNode(p0, p1, p2, p3, m_all_nodes.size(), invisible, ai_ignore); + m_max.max(p0);m_max.max(p1);m_max.max(p2);m_max.max(p3); + m_min.min(p0);m_min.min(p1);m_min.min(p2);m_min.min(p3); + m_all_nodes.push_back(node); + } + delete quad; + const XMLNode *xml = file_manager->createXMLTree(filename); if(!xml) { // No graph file exist, assume a default loop X -> X+1 - // i.e. each quad is part of the graph exactly once. - // First create an empty graph node for each quad: - for(unsigned int i=0; igetNumberOfQuads(); i++) - m_all_nodes.push_back(new GraphNode(i, (unsigned int) m_all_nodes.size())); - // Then set the default loop: + // Set the default loop: setDefaultSuccessors(); computeDirectionData(); @@ -120,29 +218,13 @@ void QuadGraph::load(const std::string &filename) for(unsigned int node_index=0; node_indexgetNumNodes(); node_index++) { const XMLNode *xml_node = xml->getNode(node_index); - // First graph node definitions: - // ----------------------------- + // Load the definition of edges between the graph nodes: + // ----------------------------------------------------- if(xml_node->getName()=="node-list") { - // A list of quads is connected to a list of graph nodes: - unsigned int from, to; - xml_node->get("from-quad", &from); - xml_node->get("to-quad", &to); - for(unsigned int i=from; i<=to; i++) - { - m_all_nodes.push_back(new GraphNode(i, (unsigned int) m_all_nodes.size())); - } + // Each quad is part of the graph exactly once now. + continue; } - else if(xml_node->getName()=="node") - { - // A single quad is connected to a single graph node. - unsigned int id; - xml_node->get("quad", &id); - m_all_nodes.push_back(new GraphNode(id, (unsigned int) m_all_nodes.size())); - } - - // Then the definition of edges between the graph nodes: - // ----------------------------------------------------- else if(xml_node->getName()=="edge-loop") { // A closed loop: @@ -202,11 +284,6 @@ void QuadGraph::load(const std::string &filename) m_lap_length = l; } - // Build unrolled quads - for (unsigned int i = 0; i < m_all_nodes.size(); i++) - { - m_all_nodes[i]->buildUnrolledQuads(m_unroll_quad_count); - } } // load // ---------------------------------------------------------------------------- @@ -439,7 +516,7 @@ void QuadGraph::computeDistanceFromStart(unsigned int node, float new_distance) if(current_distancegetQuadIndex(), delta, 0); + updateDistancesForAllSuccessors(gn->getNodeIndex(), delta, 0); } return; } @@ -456,7 +533,7 @@ void QuadGraph::computeDistanceFromStart(unsigned int node, float new_distance) if(gn_next->getDistanceFromStart()==0) continue; - computeDistanceFromStart(gn_next->getQuadIndex(), + computeDistanceFromStart(gn_next->getNodeIndex(), new_distance + gn->getDistanceToSuccessor(i)); } // for i } // computeDistanceFromStart @@ -631,21 +708,6 @@ void QuadGraph::spatialToTrack(Vec3 *dst, const Vec3& xyz, getNode(sector).getDistances(xyz, dst); } // spatialToTrack -//----------------------------------------------------------------------------- -void QuadGraph::spatialToTrackUnrolled(Vec3 *dst, const Vec3& xyz, - const int parent_sector, - const int unroll_qd_idx, - const int fork_number) const -{ - if (parent_sector == UNKNOWN_SECTOR) - { - Log::warn("Quad Graph", "UNKNOWN_SECTOR in spatialToTrackUnrolled()."); - return; - } - getNode(parent_sector).getDistancesUnrolled(xyz, fork_number, - unroll_qd_idx, dst); -} // spatialToTrackUnrolled - //----------------------------------------------------------------------------- /** findRoadSector returns in which sector on the road the position * xyz is. If xyz is not on top of the road, it sets UNKNOWN_SECTOR as sector. @@ -663,7 +725,7 @@ void QuadGraph::findRoadSector(const Vec3& xyz, int *sector, { // Most likely the kart will still be on the sector it was before, // so this simple case is tested first. - if(*sector!=UNKNOWN_SECTOR && getQuadOfNode(*sector).pointInQuad3D(xyz) ) + if(*sector!=UNKNOWN_SECTOR && getNode(*sector).pointInside(xyz) ) { return; } // if still on same quad @@ -671,9 +733,6 @@ void QuadGraph::findRoadSector(const Vec3& xyz, int *sector, // Now we search through all graph nodes, starting with // the current one int indx = *sector; - // This was used to check the vertical distance of kart from sector - // but because now karts are checked in a 3D space, this is not required - //float min_dist = 999999.9f; // If a current sector is given, and max_lookahead is specify, only test // the next max_lookahead graph nodes instead of testing the whole graph. @@ -694,13 +753,9 @@ void QuadGraph::findRoadSector(const Vec3& xyz, int *sector, indx = (*all_sectors)[i]; else indx = indx<(int)m_all_nodes.size()-1 ? indx +1 : 0; - const Quad &q = getQuadOfNode(indx); - float dist = xyz.getY() - q.getMinHeight(); - // While negative distances are unlikely, we allow some small negative - // numbers in case that the kart is partly in the track. - if(q.pointInQuad3D(xyz))// && dist < min_dist && dist>-1.0f) + const GraphNode &gn = getNode(indx); + if(gn.pointInside(xyz)) { - //min_dist = dist; *sector = indx; } } // for igetDistance2FromPoint(xyz); if(dist_2getVertices(v, color); +} // set3DVerticesOfGraph + +//----------------------------------------------------------------------------- +const bool QuadGraph::isNodeInvisible(int n) const +{ + return m_all_nodes[n]->isInvisible(); +} // isNodeInvisible + +//----------------------------------------------------------------------------- +float QuadGraph::getDistanceToNext(int n, int j) const +{ + return m_all_nodes[n]->getDistanceToSuccessor(j); +} // getDistanceToNext + +//----------------------------------------------------------------------------- +float QuadGraph::getAngleToNext(int n, int j) const +{ + return m_all_nodes[n]->getAngleToSuccessor(j); +} // getAngleToNext + +//----------------------------------------------------------------------------- +int QuadGraph::getNumberOfSuccessors(int n) const +{ + return m_all_nodes[n]->getNumberOfSuccessors(); +} // getNumberOfSuccessors + +//----------------------------------------------------------------------------- +float QuadGraph::getDistanceFromStart(int j) const +{ + return m_all_nodes[j]->getDistanceFromStart(); +} // getDistanceFromStart diff --git a/src/tracks/quad_graph.hpp b/src/tracks/quad_graph.hpp index 24937a1fe..eaa3634f7 100644 --- a/src/tracks/quad_graph.hpp +++ b/src/tracks/quad_graph.hpp @@ -23,12 +23,13 @@ #include #include -#include "tracks/graph_node.hpp" #include "tracks/graph_structure.hpp" -#include "tracks/quad_set.hpp" #include "utils/aligned_array.hpp" -class CheckLine; +#include "LinearMath/btTransform.h" + +class GraphNode; +class XMLNode; /** * \brief This class stores a graph of quads. It uses a 'simplified singleton' @@ -46,6 +47,10 @@ class QuadGraph : public GraphStructure private: static QuadGraph *m_quad_graph; + /** The 2d bounding box, used for hashing. */ + Vec3 m_min; + Vec3 m_max; + /** The actual graph data structure. */ std::vector m_all_nodes; @@ -58,9 +63,6 @@ private: /** Wether the graph should be reverted or not */ bool m_reverse; - /** Number of unrolled quads to compute per quad */ - unsigned int m_unroll_quad_count; - void setDefaultSuccessors(); void computeChecklineRequirements(GraphNode* node, int latest_checkline); void computeDirectionData(); @@ -68,7 +70,9 @@ private: float normalizeAngle(float f); void addSuccessor(unsigned int from, unsigned int to); - void load (const std::string &filename); + void load(const std::string &quad_file_name, const std::string &filename); + void getPoint(const XMLNode *xml, const std::string &attribute_name, + Vec3 *result) const; void computeDistanceFromStart(unsigned int start_node, float distance); unsigned int getStartNode() const; QuadGraph(const std::string &quad_file_name, @@ -78,14 +82,12 @@ private: // ------------------------------------------------------------------------ virtual void set3DVerticesOfGraph(int i, video::S3DVertex *v, - const video::SColor &color) const - { m_all_nodes[i]->getQuad().getVertices(v, color); } + const video::SColor &color) const; // ------------------------------------------------------------------------ virtual void getGraphBoundingBox(Vec3 *min, Vec3 *max) const - { QuadSet::get()->getBoundingBox(min, max); } + { *min = m_min; *max = m_max; } // ------------------------------------------------------------------------ - virtual const bool isNodeInvisible(int n) const - { return m_all_nodes[n]->getQuad().isInvisible(); } + virtual const bool isNodeInvisible(int n) const; // ------------------------------------------------------------------------ virtual const bool hasLapLine() const { return true; } @@ -101,12 +103,6 @@ public: bool for_ai=false) const; void spatialToTrack(Vec3 *dst, const Vec3& xyz, const int sector) const; - void spatialToTrackUnrolled(Vec3 *dst, - const Vec3& xyz, - const int parent_sector, - const int unroll_qd_idx, - const int fork_number) const; - void findRoadSector(const Vec3& XYZ, int *sector, std::vector *all_sectors=NULL) const; int findOutOfRoadSector(const Vec3& xyz, @@ -124,13 +120,13 @@ public: unsigned int count); 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 * also don't create one if it doesn't exists. */ static QuadGraph *get() { return m_quad_graph; } - // ----------------------------------------------------------------------== + // ------------------------------------------------------------------------ /** Creates a QuadGraph instance. */ static void create(const std::string &quad_file_name, const std::string &graph_file_name, @@ -156,45 +152,28 @@ public: // ------------------------------------------------------------------------ /** Returns the number of nodes in the graph. */ virtual const unsigned int getNumNodes() const - { return (unsigned int)m_all_nodes.size();} + { return m_all_nodes.size(); } // ------------------------------------------------------------------------ /** Return the distance to the j-th successor of node n. */ - float getDistanceToNext(int n, int j) const - { return m_all_nodes[n]->getDistanceToSuccessor(j);} + float getDistanceToNext(int n, int j) const; // ------------------------------------------------------------------------ /** Returns the angle of the line between node n and its j-th. * successor. */ - float getAngleToNext(int n, int j) const - { return m_all_nodes[n]->getAngleToSuccessor(j); } + float getAngleToNext(int n, int j) const; // ------------------------------------------------------------------------ /** Returns the number of successors of a node n. */ - int getNumberOfSuccessors(int n) const - { return m_all_nodes[n]->getNumberOfSuccessors(); } + int getNumberOfSuccessors(int n) const; // ------------------------------------------------------------------------ /** Returns the quad that belongs to a graph node. */ - const Quad& getQuadOfNode(unsigned int j) const - { return QuadSet::get()->getQuad(m_all_nodes[j]->getQuadIndex()); } - // ------------------------------------------------------------------------ - /** Returns the quad that belongs to a graph node. */ - GraphNode& getNode(unsigned int j) const{ return *m_all_nodes[j]; } + GraphNode& getNode(unsigned int j) const { return *m_all_nodes[j]; } // ------------------------------------------------------------------------ /** Returns the distance from the start to the beginning of a quad. */ - float getDistanceFromStart(int j) const - { return m_all_nodes[j]->getDistanceFromStart(); } + float getDistanceFromStart(int j) const; // ------------------------------------------------------------------------ /** Returns the length of the main driveline. */ - float getLapLength() const {return m_lap_length; } + float getLapLength() const { return m_lap_length; } // ------------------------------------------------------------------------ - bool isReverse() const {return m_reverse; } - // ---------------------------------------------------------------------- - /** Returns a unrolled quad of a node. */ - const Quad& getUnrolledQuadOfNode(unsigned int node, - unsigned int fork_number, - unsigned int quad_number) - { return getNode(node).getUnrolledQuad(fork_number,quad_number); } - // ---------------------------------------------------------------------- - /** Returns the number of forward quads that are unrolled for each quad **/ - unsigned int getNumberOfUnrolledQuads() const { return m_unroll_quad_count; } + bool isReverse() const { return m_reverse; } }; // QuadGraph diff --git a/src/tracks/quad_set.cpp b/src/tracks/quad_set.cpp deleted file mode 100644 index 0fa8d2d34..000000000 --- a/src/tracks/quad_set.cpp +++ /dev/null @@ -1,124 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009-2015 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "tracks/quad_set.hpp" - -#include -#include - -#include "io/file_manager.hpp" -#include "io/xml_node.hpp" -#include "utils/string_utils.hpp" - -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() -{ - m_quad_set = this; -} // QuadSet - -// ----------------------------------------------------------------------------- -/** Destructor, frees all memory. - */ -QuadSet::~QuadSet() -{ - for(unsigned int i=0; iget(attribute_name, &s); - int pos=(int)s.find_first_of(":"); - if(pos>0) // n:p specification - { - std::vector l = StringUtils::split(s, ':'); - int n=atoi(l[0].c_str()); - int p=atoi(l[1].c_str()); - *result=(*m_all_quads[n])[p]; - } - else - { - xml->get(attribute_name, result); - } - -} // getPoint -// ----------------------------------------------------------------------------- -/** Loads the set of all quads from the specified 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); - - XMLNode *xml = file_manager->createXMLTree(filename); - if(!xml || xml->getName()!="quads") - { - Log::error("[QuadSet::load] ERROR : QuadSet '%s' not found.", filename.c_str()); - delete xml; - return; - } - for(unsigned int i=0; igetNumNodes(); i++) - { - const XMLNode *xml_node = xml->getNode(i); - if(xml_node->getName()!="quad") - { - Log::warn("[QuadSet::load] WARNING: Unsupported node type '%s' found in '%s' - ignored.", - xml_node->getName().c_str(), filename.c_str()); - continue; - } - - // Note that it's not easy to do the reading of the parameters here - // in quad, since the specification in the xml can contain references - // to previous points. E.g.: - // get("invisible", &invisible); - bool ai_ignore=false; - xml_node->get("ai-ignore", &ai_ignore); - Quad* q=new Quad(p0,p1,p2,p3, invisible, ai_ignore); - m_all_quads.push_back(q); - m_max.max(p0);m_max.max(p1);m_max.max(p2);m_max.max(p3); - m_min.min(p0);m_min.min(p1);m_min.min(p2);m_min.min(p3); - - } - delete xml; -} // load - -// ----------------------------------------------------------------------------- diff --git a/src/tracks/quad_set.hpp b/src/tracks/quad_set.hpp deleted file mode 100644 index 8e646ae4e..000000000 --- a/src/tracks/quad_set.hpp +++ /dev/null @@ -1,90 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009-2015 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef HEADER_QUAD_SET_HPP -#define HEADER_QUAD_SET_HPP - -#include -#include - -#include "tracks/quad.hpp" -#include "utils/vec3.hpp" - -class XMLNode; - -/** - * \ingroup tracks - */ -class QuadSet -{ -private: - /** The 2d bounding box, used for hashing. */ - Vec3 m_min; - Vec3 m_max; - - /** The list of all quads. */ - std::vector m_all_quads; - - /** 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; - - 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 -#endif diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index 38c01d387..0b215a1d0 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -32,6 +32,7 @@ #include "graphics/glwrap.hpp" #include "graphics/irr_driver.hpp" #include "graphics/lod_node.hpp" +#include "graphics/material.hpp" #include "graphics/material_manager.hpp" #include "graphics/mesh_tools.hpp" #include "graphics/moving_texture.hpp" @@ -54,10 +55,10 @@ #include "tracks/bezier_curve.hpp" #include "tracks/battle_graph.hpp" #include "tracks/check_manager.hpp" +#include "tracks/graph_node.hpp" #include "tracks/model_definition_loader.hpp" #include "tracks/track_manager.hpp" #include "tracks/quad_graph.hpp" -#include "tracks/quad_set.hpp" #include "tracks/track_object_manager.hpp" #include "utils/constants.hpp" #include "utils/log.hpp" @@ -2291,8 +2292,8 @@ void Track::itemCommand(const XMLNode *node) // If a valid road_sector is not found if (road_sector == QuadGraph::UNKNOWN_SECTOR) road_sector = QuadGraph::get()->findOutOfRoadSector(xyz, road_sector); - Vec3 quadnormal = QuadGraph::get()->getQuadOfNode(road_sector).getNormal(); - + const Vec3& quadnormal = QuadGraph::get()->getNode(road_sector).getNormal(); + const Material *m; Vec3 hit_point; m_track_mesh->castRay(loc, loc + -1.0f*quadnormal, &hit_point, &m, &normal); @@ -2384,7 +2385,7 @@ bool Track::findGround(AbstractKart *kart) { int sector = ((LinearWorld*)World::getWorld())->getTrackSector(kart->getWorldKartId()).getCurrentGraphNode(); if (sector != QuadGraph::UNKNOWN_SECTOR) - quadNormal = QuadGraph::get()->getQuadOfNode(sector).getNormal(); + quadNormal = QuadGraph::get()->getNode(sector).getNormal(); } to = to + -1000.0f*quadNormal; @@ -2436,3 +2437,7 @@ bool Track::findGround(AbstractKart *kart) return true; } // findGround + + +float Track::getTrackLength() const {return QuadGraph::get()->getLapLength();} +float Track::getAngle(int n) const { return QuadGraph::get()->getAngleToNext(n, 0); } \ No newline at end of file diff --git a/src/tracks/track.hpp b/src/tracks/track.hpp index 185bdd759..f258ac997 100644 --- a/src/tracks/track.hpp +++ b/src/tracks/track.hpp @@ -26,31 +26,31 @@ * objects. */ +#include #include #include namespace irr { - namespace video { class ITexture; class SColor; } - namespace scene { class IMesh; class ILightSceneNode; } + namespace video { class ITexture; } + namespace scene { class IMesh; class ILightSceneNode; class ISceneNode; } } using namespace irr; class ModelDefinitionLoader; #include "LinearMath/btTransform.h" -#include "graphics/material.hpp" -#include "items/item.hpp" #include "scriptengine/script_engine.hpp" -#include "tracks/quad_graph.hpp" #include "utils/aligned_array.hpp" #include "utils/translation.hpp" #include "utils/vec3.hpp" #include "utils/ptr_vector.hpp" +class AbstractKart; class AnimationManager; class BezierCurve; class CheckManager; +class FrameBuffer; class MovingTexture; class MusicInformation; class ParticleEmitter; @@ -473,8 +473,7 @@ public: int getVersion () const {return m_version; } // ------------------------------------------------------------------------ /** Returns the length of the main driveline. */ - float getTrackLength () const - {return QuadGraph::get()->getLapLength();} + float getTrackLength () const; // ------------------------------------------------------------------------ /** Returns a unique identifier for this track (the directory name). */ const std::string& getIdent () const {return m_ident; } @@ -511,8 +510,7 @@ public: * in the direction of the default way on the track. * \param n Number of the quad for which the angle is asked. */ - float getAngle(int n) const - { return QuadGraph::get()->getAngleToNext(n, 0); } + float getAngle(int n) const; // ------------------------------------------------------------------------ /** Returns the 2d coordinates of a point when drawn on the mini map * texture. diff --git a/src/tracks/track_sector.cpp b/src/tracks/track_sector.cpp index 279511bc8..a7a325e9d 100644 --- a/src/tracks/track_sector.cpp +++ b/src/tracks/track_sector.cpp @@ -20,9 +20,9 @@ #include "modes/world.hpp" #include "tracks/check_manager.hpp" #include "tracks/check_structure.hpp" -#include "tracks/track.hpp" #include "tracks/track_sector.hpp" #include "tracks/quad_graph.hpp" +#include "tracks/graph_node.hpp" // ---------------------------------------------------------------------------- /** Initialises the object, and sets the current graph node to be undefined. @@ -65,7 +65,7 @@ void TrackSector::update(const Vec3 &xyz) { // keep the current quad as the latest valid one IF the player has one // of the required checklines - GraphNode gn = QuadGraph::get()->getNode(m_current_graph_node); + const GraphNode& gn = QuadGraph::get()->getNode(m_current_graph_node); const std::vector& checkline_requirements = gn.getChecklineRequirements(); if (checkline_requirements.size() == 0) From 9d41f4b4296e5e397ad4d9b9d25f27537d7d32a5 Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 4 Sep 2016 13:15:51 +0800 Subject: [PATCH 150/350] Add assert check for graph.xml --- src/karts/controller/test_ai.hpp | 1 + src/tracks/navmesh.cpp | 8 -------- src/tracks/quad_graph.cpp | 3 +++ 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/karts/controller/test_ai.hpp b/src/karts/controller/test_ai.hpp index 8d1ffb277..ff7f73f32 100644 --- a/src/karts/controller/test_ai.hpp +++ b/src/karts/controller/test_ai.hpp @@ -66,6 +66,7 @@ namespace irr * stage) identical to the Skidding AI. \ingroup controller */ + class TestAI : public AIBaseLapController { private: diff --git a/src/tracks/navmesh.cpp b/src/tracks/navmesh.cpp index 1da2bc574..74d525add 100644 --- a/src/tracks/navmesh.cpp +++ b/src/tracks/navmesh.cpp @@ -95,14 +95,6 @@ NavMesh::NavMesh(const std::string &filename) m_quads.push_back(new Quad( all_vertices[quad_index[0]], all_vertices[quad_index[1]], all_vertices[quad_index[2]], all_vertices[quad_index[3]])); - -/*(AbstractNode* tn = createNode( - all_vertices[quad_index[0]], all_vertices[quad_index[1]], - all_vertices[quad_index[2]], all_vertices[quad_index[3]],false,false); - -Vec3 pp(0,0,0); -tn->pointInNode(pp);*/ - } } } diff --git a/src/tracks/quad_graph.cpp b/src/tracks/quad_graph.cpp index 00cdb3f72..e0467e570 100644 --- a/src/tracks/quad_graph.cpp +++ b/src/tracks/quad_graph.cpp @@ -223,6 +223,9 @@ void QuadGraph::load(const std::string &quad_file_name, if(xml_node->getName()=="node-list") { // Each quad is part of the graph exactly once now. + unsigned int to = 0; + xml_node->get("to-quad", &to); + assert(to + 1 == m_all_nodes.size()); continue; } else if(xml_node->getName()=="edge-loop") From 262eb40dfed47d1002dc7ad1790764fb20fcaf3d Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 4 Sep 2016 14:33:23 +0800 Subject: [PATCH 151/350] Allow showing yellow (2d) / green (3d) quads in track debug --- src/tracks/battle_graph.cpp | 1 - src/tracks/graph_structure.cpp | 12 +++++------- src/tracks/graph_structure.hpp | 7 +++++-- src/tracks/quad_graph.cpp | 14 ++++++++++++++ src/tracks/quad_graph.hpp | 3 +-- 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/tracks/battle_graph.cpp b/src/tracks/battle_graph.cpp index 300b38e8c..e6ebcc2ab 100644 --- a/src/tracks/battle_graph.cpp +++ b/src/tracks/battle_graph.cpp @@ -1,4 +1,3 @@ - // // SuperTuxKart - a fun racing game with go-kart // Copyright (C) 2009-2015 Joerg Henrichs diff --git a/src/tracks/graph_structure.cpp b/src/tracks/graph_structure.cpp index bb006e489..e6fd0cd71 100644 --- a/src/tracks/graph_structure.cpp +++ b/src/tracks/graph_structure.cpp @@ -78,15 +78,11 @@ void GraphStructure::createDebugMesh() createMesh(/*show_invisible*/true, /*enable_transparency*/true); - // Now colour the quads red/blue/red ... - video::SColor c( 128, 255, 0, 0); video::S3DVertex *v = (video::S3DVertex*)m_mesh_buffer->getVertices(); for (unsigned int i = 0; i < m_mesh_buffer->getVertexCount(); i++) { - // Swap the colours from red to blue and back - c.setRed ((i%2) ? 255 : 0); - c.setBlue((i%2) ? 0 : 255); - v[i].Color = c; + // Swap the alpha and back + v[i].Color.setAlpha((i%2) ? 64 : 255); } m_node = irr_driver->addMesh(m_mesh, "track-debug-mesh"); #ifdef DEBUG @@ -154,7 +150,9 @@ void GraphStructure::createMesh(bool show_invisible, // Transfer the 4 points of the current quad to the list of vertices set3DVerticesOfGraph(count, new_v+4*i, (different_color ? (nc == COLOR_RED ? video::SColor(255, 255, 0, 0) : - video::SColor(255, 0, 0, 255)) : c)); + nc == COLOR_GREEN ? video::SColor(255, 0, 255, 0) : + nc == COLOR_BLUE ? video::SColor(255, 0, 0, 255) : + video::SColor(255, 255, 255, 0)) : c)); // Set up the indices for the triangles // (note, afaik with opengl we could use quads directly, but the code diff --git a/src/tracks/graph_structure.hpp b/src/tracks/graph_structure.hpp index d66f156c5..e6f159e4c 100644 --- a/src/tracks/graph_structure.hpp +++ b/src/tracks/graph_structure.hpp @@ -46,11 +46,14 @@ class GraphStructure : public NoCopy { protected: - /** Used by soccer field with navmesh to draw goal line. */ + /** Used by soccer field with navmesh to draw goal line, + * or to determine 2d/3d nodes in driveline graph. */ enum NodeColor { COLOR_BLUE, - COLOR_RED + COLOR_GREEN, + COLOR_RED, + COLOR_YELLOW }; void cleanupDebugMesh(); diff --git a/src/tracks/quad_graph.cpp b/src/tracks/quad_graph.cpp index e0467e570..f8d3c86ca 100644 --- a/src/tracks/quad_graph.cpp +++ b/src/tracks/quad_graph.cpp @@ -908,3 +908,17 @@ float QuadGraph::getDistanceFromStart(int j) const { return m_all_nodes[j]->getDistanceFromStart(); } // getDistanceFromStart + +//----------------------------------------------------------------------------- +const bool QuadGraph::differentNodeColor(int n, NodeColor* c) const +{ + if (UserConfigParams::m_track_debug) + { + if (dynamic_cast(m_all_nodes[n]) != NULL) + *c = COLOR_GREEN; + else + *c = COLOR_YELLOW; + return true; + } + return false; +} // differentNodeColor diff --git a/src/tracks/quad_graph.hpp b/src/tracks/quad_graph.hpp index eaa3634f7..a5ead4155 100644 --- a/src/tracks/quad_graph.hpp +++ b/src/tracks/quad_graph.hpp @@ -92,8 +92,7 @@ private: virtual const bool hasLapLine() const { return true; } // ------------------------------------------------------------------------ - virtual const bool differentNodeColor(int n, NodeColor* c) const - { return false; } + virtual const bool differentNodeColor(int n, NodeColor* c) const; public: static const int UNKNOWN_SECTOR; From a56326698817fd16620593dd0da37e7c652554d0 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 5 Sep 2016 08:00:21 +1000 Subject: [PATCH 152/350] Made the KartRewinder to be a kart (which gives it easier access to some of the kart attributes that need to be saved). --- src/items/attachment.cpp | 2 +- src/items/attachment.hpp | 2 +- src/items/powerup.cpp | 2 +- src/items/powerup.hpp | 2 +- src/karts/controller/kart_control.hpp | 2 +- src/karts/kart.cpp | 8 ------- src/karts/kart.hpp | 5 +---- src/karts/kart_rewinder.cpp | 30 +++++++++++++++++---------- src/karts/kart_rewinder.hpp | 16 ++++++++------ src/modes/world.cpp | 9 ++++++-- src/network/rewinder.hpp | 3 +++ 11 files changed, 45 insertions(+), 36 deletions(-) diff --git a/src/items/attachment.cpp b/src/items/attachment.cpp index 6875f61b0..2ee53a80d 100644 --- a/src/items/attachment.cpp +++ b/src/items/attachment.cpp @@ -231,7 +231,7 @@ void Attachment::clear() /** Saves the attachment state. Called as part of the kart saving its state. * \param buffer The kart rewinder's state buffer. */ -void Attachment::saveState(BareNetworkString *buffer) +void Attachment::saveState(BareNetworkString *buffer) const { // We use bit 7 to indicate if a previous owner is defined for a bomb assert(ATTACH_MAX<=127); diff --git a/src/items/attachment.hpp b/src/items/attachment.hpp index 4f0d5243f..a19c0c23b 100644 --- a/src/items/attachment.hpp +++ b/src/items/attachment.hpp @@ -118,7 +118,7 @@ public: AbstractKart *previous_kart=NULL); virtual void rewind(BareNetworkString *buffer); void rewindTo(BareNetworkString *buffer); - void saveState(BareNetworkString *buffer); + void saveState(BareNetworkString *buffer) const; // ------------------------------------------------------------------------ /** Sets the type of the attachment, but keeps the old time left value. */ diff --git a/src/items/powerup.cpp b/src/items/powerup.cpp index 14e026255..2b4f4f6a4 100644 --- a/src/items/powerup.cpp +++ b/src/items/powerup.cpp @@ -74,7 +74,7 @@ void Powerup::reset() * state or when a new powerup even is saved. * \param buffer The buffer into which to save the state. */ -void Powerup::saveState(BareNetworkString *buffer) +void Powerup::saveState(BareNetworkString *buffer) const { buffer->addUInt8(uint8_t(m_type)); if(m_type!=PowerupManager::POWERUP_NOTHING) diff --git a/src/items/powerup.hpp b/src/items/powerup.hpp index a64e16b61..6e88a520c 100644 --- a/src/items/powerup.hpp +++ b/src/items/powerup.hpp @@ -60,7 +60,7 @@ public: void adjustSound (); void use (); void hitBonusBox (const Item &item, int newC=-1); - void saveState(BareNetworkString *buffer); + void saveState(BareNetworkString *buffer) const; void rewindTo(BareNetworkString *buffer); diff --git a/src/karts/controller/kart_control.hpp b/src/karts/controller/kart_control.hpp index 06bc8784e..438132cd5 100644 --- a/src/karts/controller/kart_control.hpp +++ b/src/karts/controller/kart_control.hpp @@ -100,7 +100,7 @@ public: static int getLength() { return 9; } // ------------------------------------------------------------------------ /** Copies the important data from this objects into a memory buffer. */ - void copyToBuffer(BareNetworkString *buffer) + void copyToBuffer(BareNetworkString *buffer) const { buffer->add(m_steer); buffer->add(m_accel); diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 37be5f118..9f83f0b82 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -232,7 +232,6 @@ void Kart::init(RaceManager::KartType type) // Create the stars effect m_stars_effect = new Stars(this); - m_rewinder = new KartRewinder(this); reset(); } // init @@ -243,8 +242,6 @@ void Kart::init(RaceManager::KartType type) */ Kart::~Kart() { - delete m_rewinder; - // Delete all custom sounds (TODO: add back when properly done) /* for (int n = 0; n < SFXManager::NUM_CUSTOMS; n++) @@ -412,9 +409,6 @@ void Kart::reset() if(m_controller) m_controller->reset(); - // Must be called after the controls are reset - m_rewinder->reset(); - // 3 strikes mode can hide the wheels scene::ISceneNode** wheels = getKartModel()->getWheelNodes(); if(wheels[0]) wheels[0]->setVisible(true); @@ -1496,8 +1490,6 @@ void Kart::update(float dt) } } - // Store current controls if there was any change from the previous state: - m_rewinder->update(); } // update //----------------------------------------------------------------------------- diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index 152b676c3..16ef6c65f 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -76,7 +76,7 @@ protected: /** Is time flying activated */ bool m_is_jumping; -private: +protected: /** Handles speed increase and capping due to powerup, terrain, ... */ MaxSpeed *m_max_speed; @@ -199,9 +199,6 @@ private: float m_view_blocked_by_plunger; float m_speed; - /** The rewinder object for network play. */ - KartRewinder *m_rewinder; - std::vector m_custom_sounds; SFXBase *m_beep_sound; SFXBase *m_engine_sound; diff --git a/src/karts/kart_rewinder.cpp b/src/karts/kart_rewinder.cpp index aa45a4151..2c7ff0196 100644 --- a/src/karts/kart_rewinder.cpp +++ b/src/karts/kart_rewinder.cpp @@ -27,9 +27,14 @@ #include -KartRewinder::KartRewinder(AbstractKart *kart) : Rewinder(/*can_be_destroyed*/ false) +KartRewinder::KartRewinder(const std::string& ident,unsigned int world_kart_id, + int position, const btTransform& init_transform, + PerPlayerDifficulty difficulty, + RenderInfo::KartRenderType krt) + : Rewinder(/*can_be_destroyed*/ false) + , Kart(ident, world_kart_id, position, init_transform, difficulty, + krt) { - m_kart = kart; } // KartRewinder // ---------------------------------------------------------------------------- @@ -37,6 +42,8 @@ KartRewinder::KartRewinder(AbstractKart *kart) : Rewinder(/*can_be_destroyed*/ f */ void KartRewinder::reset() { + Kart::reset(); + Rewinder::reset(); } // reset // ---------------------------------------------------------------------------- @@ -52,7 +59,7 @@ BareNetworkString* KartRewinder::saveState() const const int MEMSIZE = 13*sizeof(float) + 9+2; BareNetworkString *buffer = new BareNetworkString(MEMSIZE); - const btRigidBody *body = m_kart->getBody(); + const btRigidBody *body = getBody(); // 1) Physics values: transform and velocities // ------------------------------------------- @@ -65,15 +72,15 @@ BareNetworkString* KartRewinder::saveState() const // 2) Steering and other player controls // ------------------------------------- - m_kart->getControls().copyToBuffer(buffer); + getControls().copyToBuffer(buffer); // 3) Attachment // ------------- - m_kart->getAttachment()->saveState(buffer); + getAttachment()->saveState(buffer); // 4) Powerup // ---------- - m_kart->getPowerup()->saveState(buffer); + getPowerup()->saveState(buffer); return buffer; } // saveState @@ -88,22 +95,22 @@ void KartRewinder::rewindToState(BareNetworkString *buffer) btTransform t; t.setOrigin(buffer->getVec3()); t.setRotation(buffer->getQuat()); - btRigidBody *body = m_kart->getBody(); + btRigidBody *body = getBody(); body->proceedToTransform(t); body->setLinearVelocity(buffer->getVec3()); body->setAngularVelocity(buffer->getVec3()); // 2) Steering and other controls // ------------------------------ - m_kart->getControls().setFromBuffer(buffer); + getControls().setFromBuffer(buffer); // 3) Attachment // ------------- - m_kart->getAttachment()->rewindTo(buffer); + getAttachment()->rewindTo(buffer); // 4) Powerup // ---------- - m_kart->getPowerup()->rewindTo(buffer); + getPowerup()->rewindTo(buffer); return; } // rewindToState @@ -111,8 +118,9 @@ void KartRewinder::rewindToState(BareNetworkString *buffer) /** Called once a frame. It will add a new kart control event to the rewind * manager if any control values have changed. */ -void KartRewinder::update() +void KartRewinder::update(float dt) { + Kart::update(dt); } // update // ---------------------------------------------------------------------------- diff --git a/src/karts/kart_rewinder.hpp b/src/karts/kart_rewinder.hpp index ae279fc2b..44428fae1 100644 --- a/src/karts/kart_rewinder.hpp +++ b/src/karts/kart_rewinder.hpp @@ -19,28 +19,33 @@ #ifndef HEADER_KART_REWINDER_HPP #define HEADER_KART_REWINDER_HPP +#include "karts/kart.hpp" #include "network/rewinder.hpp" #include "utils/cpp2011.hpp" class AbstractKart; class BareNetworkString; -class KartRewinder : public Rewinder +class KartRewinder : public Rewinder, public Kart { private: - /** Pointer to the original kart object. */ - AbstractKart *m_kart; // Flags to indicate the different event types enum { EVENT_CONTROL = 0x01, EVENT_ATTACH = 0x02 }; public: - KartRewinder(AbstractKart *kart); + KartRewinder(const std::string& ident, + unsigned int world_kart_id, + int position, const btTransform& init_transform, + PerPlayerDifficulty difficulty, + RenderInfo::KartRenderType krt = + RenderInfo::KRT_DEFAULT); virtual ~KartRewinder() {}; virtual BareNetworkString* saveState() const; void reset(); virtual void rewindToState(BareNetworkString *p) OVERRIDE; virtual void rewindToEvent(BareNetworkString *p) OVERRIDE; + virtual void update(float dt); // ------------------------------------------------------------------------- virtual void undoState(BareNetworkString *p) OVERRIDE @@ -54,8 +59,7 @@ public: // ------------------------------------------------------------------------- - void update(); - + }; // Rewinder #endif diff --git a/src/modes/world.cpp b/src/modes/world.cpp index f36ca0685..a87a5d11b 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -40,6 +40,7 @@ #include "karts/controller/network_player_controller.hpp" #include "karts/kart.hpp" #include "karts/kart_properties_manager.hpp" +#include "karts/kart_rewinder.hpp" #include "modes/overworld.hpp" #include "modes/profile_world.hpp" #include "modes/soccer_world.hpp" @@ -338,8 +339,12 @@ AbstractKart *World::createKart(const std::string &kart_ident, int index, int position = index+1; btTransform init_pos = getStartTransform(index - gk); - AbstractKart *new_kart = new Kart(kart_ident, index, position, init_pos, - difficulty); + AbstractKart *new_kart; + if (RewindManager::get()->isEnabled()) + new_kart = new KartRewinder(kart_ident, index, position, init_pos, + difficulty); + else + new_kart = new Kart(kart_ident, index, position, init_pos, difficulty); new_kart->init(race_manager->getKartType(index)); Controller *controller = NULL; switch(kart_type) diff --git a/src/network/rewinder.hpp b/src/network/rewinder.hpp index d8b1a8b97..f4d116887 100644 --- a/src/network/rewinder.hpp +++ b/src/network/rewinder.hpp @@ -58,6 +58,9 @@ public: */ virtual void undoState(BareNetworkString *buffer) = 0; + // ------------------------------------------------------------------------- + /** Nothing to do here. */ + virtual void reset() {}; // ------------------------------------------------------------------------- /** True if this rewinder can be destroyed. Karts can not be destroyed, * cakes can. This is used by the RewindManager in reset. */ From 1860edca4e6dd9dee879a572d80293af070871b8 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 5 Sep 2016 08:14:51 +1000 Subject: [PATCH 153/350] Save startup-boost information in state. --- src/karts/kart_rewinder.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/karts/kart_rewinder.cpp b/src/karts/kart_rewinder.cpp index 2c7ff0196..16721d830 100644 --- a/src/karts/kart_rewinder.cpp +++ b/src/karts/kart_rewinder.cpp @@ -56,7 +56,7 @@ void KartRewinder::reset() */ BareNetworkString* KartRewinder::saveState() const { - const int MEMSIZE = 13*sizeof(float) + 9+2; + const int MEMSIZE = 13*sizeof(float) + 9+3; BareNetworkString *buffer = new BareNetworkString(MEMSIZE); const btRigidBody *body = getBody(); @@ -69,6 +69,7 @@ BareNetworkString* KartRewinder::saveState() const buffer->add(q); buffer->add(body->getLinearVelocity()); buffer->add(body->getAngularVelocity()); + buffer->addUInt8(m_has_started); // necessary for startup speed boost // 2) Steering and other player controls // ------------------------------------- @@ -99,6 +100,7 @@ void KartRewinder::rewindToState(BareNetworkString *buffer) body->proceedToTransform(t); body->setLinearVelocity(buffer->getVec3()); body->setAngularVelocity(buffer->getVec3()); + m_has_started = buffer->getUInt8(); // necessary for startup speed boost // 2) Steering and other controls // ------------------------------ From 77ae30a0c54a1738ed6f929c9eaea42552ad2aaf Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 5 Sep 2016 09:59:29 +1000 Subject: [PATCH 154/350] Avoid compiler warning. --- src/karts/kart_rewinder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/karts/kart_rewinder.cpp b/src/karts/kart_rewinder.cpp index 16721d830..7275f9503 100644 --- a/src/karts/kart_rewinder.cpp +++ b/src/karts/kart_rewinder.cpp @@ -100,7 +100,7 @@ void KartRewinder::rewindToState(BareNetworkString *buffer) body->proceedToTransform(t); body->setLinearVelocity(buffer->getVec3()); body->setAngularVelocity(buffer->getVec3()); - m_has_started = buffer->getUInt8(); // necessary for startup speed boost + m_has_started = buffer->getUInt8()!=0; // necessary for startup speed boost // 2) Steering and other controls // ------------------------------ From 7d7b98564f07358cf9ce529b1786cf016a65e169 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 5 Sep 2016 17:54:45 +1000 Subject: [PATCH 155/350] Fixed speed smoothing factor (which hopefully fixes the shaking camera). --- src/karts/kart.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 9f83f0b82..7e6414a70 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -2181,7 +2181,7 @@ void Kart::updatePhysics(float dt) // gets rid of this jitter, and also r float old_speed = m_speed; m_speed = getVehicle()->getRigidBody()->getLinearVelocity().length(); - float f=1.0f; + float f=0.3f; m_speed = f*m_speed + (1.0f-f)*old_speed; // calculate direction of m_speed From 6245cea609f74fa6e6f49daf5500f2db816fe9ce Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 7 Sep 2016 08:15:09 +1000 Subject: [PATCH 156/350] Added MaxSpeed to saved state of each kart. --- src/karts/kart_rewinder.cpp | 13 +++- src/karts/max_speed.cpp | 130 +++++++++++++++++++++++++++++++++++- src/karts/max_speed.hpp | 38 +++++++++-- 3 files changed, 173 insertions(+), 8 deletions(-) diff --git a/src/karts/kart_rewinder.cpp b/src/karts/kart_rewinder.cpp index 7275f9503..03309bc97 100644 --- a/src/karts/kart_rewinder.cpp +++ b/src/karts/kart_rewinder.cpp @@ -17,9 +17,11 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "karts/kart_rewinder.hpp" -#include "karts/abstract_kart.hpp" + #include "items/attachment.hpp" #include "items/powerup.hpp" +#include "karts/abstract_kart.hpp" +#include "karts/max_speed.hpp" #include "modes/world.hpp" #include "network/rewind_manager.hpp" #include "network/network_string.hpp" @@ -82,6 +84,11 @@ BareNetworkString* KartRewinder::saveState() const // 4) Powerup // ---------- getPowerup()->saveState(buffer); + + // 5) Max speed info + // ------------------ + m_max_speed->saveState(buffer); + return buffer; } // saveState @@ -113,6 +120,10 @@ void KartRewinder::rewindToState(BareNetworkString *buffer) // 4) Powerup // ---------- getPowerup()->rewindTo(buffer); + + // 5) Max speed info + // ------------------ + m_max_speed->rewindTo(buffer); return; } // rewindToState diff --git a/src/karts/max_speed.cpp b/src/karts/max_speed.cpp index 377720d81..54bcc345e 100644 --- a/src/karts/max_speed.cpp +++ b/src/karts/max_speed.cpp @@ -135,8 +135,7 @@ void MaxSpeed::instantSpeedIncrease(unsigned int category, m_kart->getVehicle()->instantSpeedIncreaseTo(speed); -} -// instantSpeedIncrease +} // instantSpeedIncrease // ---------------------------------------------------------------------------- /** Handles the update of speed increase objects. The m_duration variable @@ -161,6 +160,33 @@ void MaxSpeed::SpeedIncrease::update(float dt) m_current_speedup -= dt*m_max_add_speed/m_fade_out_time; } // SpeedIncrease::update +// ---------------------------------------------------------------------------- +void MaxSpeed::SpeedIncrease::saveState(BareNetworkString *buffer) const +{ + buffer->addFloat(m_max_add_speed); + buffer->addFloat(m_duration); + buffer->addFloat(m_fade_out_time); + buffer->addFloat(m_current_speedup); + buffer->addFloat(m_engine_force); +} // saveState + +// ---------------------------------------------------------------------------- +void MaxSpeed::SpeedIncrease::rewindTo(BareNetworkString *buffer, + bool is_active) +{ + if(is_active) + { + m_max_add_speed = buffer->getFloat(); + m_duration = buffer->getFloat(); + m_current_speedup = buffer->getFloat(); + m_engine_force = buffer->getFloat(); + } + else // make sure to disable this category + { + reset(); + } +} // restoreState + // ---------------------------------------------------------------------------- /** Defines a slowdown, which is in fraction of top speed. * \param category The category for which the speed is increased. @@ -210,6 +236,38 @@ void MaxSpeed::SpeedDecrease::update(float dt) m_current_fraction = m_max_speed_fraction; } // SpeedDecrease::update +// ---------------------------------------------------------------------------- +/** Saves the state of an (active) speed decrease category. It is not called + * if the speed decrease is not active. + * \param buffer Buffer which will store the state information. + */ +void MaxSpeed::SpeedDecrease::saveState(BareNetworkString *buffer) const +{ + buffer->addFloat(m_max_speed_fraction); + buffer->addFloat(m_fade_in_time); + buffer->addFloat(m_current_fraction); + buffer->addFloat(m_duration); +} // saveState + +// ---------------------------------------------------------------------------- +/** Restores a previously saved state for an active speed decrease category. + */ +void MaxSpeed::SpeedDecrease::rewindTo(BareNetworkString *buffer, + bool is_active) +{ + if(is_active) + { + m_max_speed_fraction = buffer->getFloat(); + m_fade_in_time = buffer->getFloat(); + m_current_fraction = buffer->getFloat(); + m_duration = buffer->getFloat(); + } + else // make sure it is not active + { + reset(); + } +} // restoreState + // ---------------------------------------------------------------------------- /** Returns how much increased speed time is left over in the given category. * \param category Which category to report on. @@ -267,3 +325,71 @@ void MaxSpeed::update(float dt) } // update // ---------------------------------------------------------------------------- +/** Saves the speed data in a network string for rewind. + * \param buffer Pointer to the network string to store the data. + */ +void MaxSpeed::saveState(BareNetworkString *buffer) const +{ + // Save the slowdown states + // ------------------------ + // Get the bit pattern of all active slowdowns + uint8_t active_slowdown = 0; + for(unsigned int i=MS_DECREASE_MIN; iaddUInt8(active_slowdown); + + for(unsigned int i=MS_DECREASE_MIN, b=1; isaveState(buffer); + } + + // Now save the speedup state + // -------------------------- + // Get the bit pattern of all active speedups + uint8_t active_speedups = 0; + for(unsigned int i=MS_INCREASE_MIN; iaddUInt8(active_speedups); + for(unsigned int i=MS_INCREASE_MIN, b=1; igetUInt8(); + + for(unsigned int i=MS_DECREASE_MIN, b=1; irewindTo(buffer, (active_slowdown & b) == b); + } + + // Restore the speedup state + // -------------------------- + // Get the bit pattern of all active speedups + uint8_t active_speedups = buffer->getUInt8(); + for(unsigned int i=MS_INCREASE_MIN, b=1; i 0 ? m_engine_force : 0; - } + } // getEngineForce + // -------------------------------------------------------------------- + /** Returns if this speed increase is active atm. */ + bool isActive() const { return m_duration > -m_fade_out_time; } }; // SpeedIncrease // ------------------------------------------------------------------------ @@ -126,17 +139,30 @@ private: /** The constructor initialises the data with data that won't * affect top speed at all. */ SpeedDecrease() + { + reset(); + } // SpeedDecrease + // -------------------------------------------------------------------- + /** Resets the state to be inactive. */ + void reset() { m_max_speed_fraction = 1.0f; m_fade_in_time = 0.0f; m_current_fraction = 1.0f; - m_duration = -1.0f; - } // SpeedDecrease + m_duration = 0.0f; + } //reset + // -------------------------------------------------------------------- void update(float dt); + void saveState(BareNetworkString *buffer) const; + void rewindTo(BareNetworkString *buffer, bool is_active); // -------------------------------------------------------------------- /** Returns the current slowdown fracftion, taking a 'fade in' * into account. */ float getSlowdownFraction() const {return m_current_fraction;} + // -------------------------------------------------------------------- + /** Returns if this speed decrease is active atm. A duration of + * -1 indicates an ongoing effect. */ + bool isActive() const { return m_duration > 0 || m_duration <= -1.0f; } }; // SpeedDecrease // ------------------------------------------------------------------------ @@ -164,6 +190,8 @@ public: float getSpeedIncreaseTimeLeft(unsigned int category); void update(float dt); void reset(); + void saveState(BareNetworkString *buffer) const; + void rewindTo(BareNetworkString *buffer); // ------------------------------------------------------------------------ /** Sets the minimum speed a kart should have. This is used to guarantee * that e.g. zippers on ramps will always fast enough for the karts to From 3a367ce9893d2c37bba721b9700c1f33953d5a47 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 7 Sep 2016 08:46:46 +1000 Subject: [PATCH 157/350] For debugging: instead of specifying the time to go back, specify the time to go back to. --- src/input/input_manager.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/input/input_manager.cpp b/src/input/input_manager.cpp index 1ecc25c9e..71fa4ef4e 100644 --- a/src/input/input_manager.cpp +++ b/src/input/input_manager.cpp @@ -277,14 +277,14 @@ void InputManager::handleStaticAction(int key, int value) case KEY_F11: if(value && shift_is_pressed && world && RewindManager::isEnabled()) { - printf("Enter rewind time:"); + printf("Enter rewind to time:"); char s[256]; fgets(s, 256, stdin); float t; StringUtils::fromString(s,t); - RewindManager::get()->rewindTo(world->getTime()-t); + RewindManager::get()->rewindTo(t); Log::info("Rewind", "Rewinding from %f to %f", - world->getTime(), world->getTime()-t); + world->getTime(), t); } break; From f2d1eb7117d086884a7cf51d32680bd4ce650628 Mon Sep 17 00:00:00 2001 From: Deve Date: Wed, 7 Sep 2016 22:39:58 +0200 Subject: [PATCH 158/350] Use high precision floating point variables if possible. On desktop medium and high precision is generally the same, at least for mesa drivers. But on some Android devices medium precision is much worse, which causes artifacts. We need high precision float variables in vertex shaders for proper scene rendering. Setting different precision for fragment and vertex shaders seems to be not possible, so we just set it to high for both shader types when it's available. --- src/graphics/shader.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/graphics/shader.cpp b/src/graphics/shader.cpp index 306561d15..408248dd6 100644 --- a/src/graphics/shader.cpp +++ b/src/graphics/shader.cpp @@ -107,11 +107,17 @@ GLuint ShaderBase::loadShader(const std::string &file, unsigned type) if (CVS->needsRGBBindlessWorkaround()) code << "#define SRGBBindlessFix\n"; +#if !defined(USE_GLES2) //shader compilation fails with some drivers if there is no precision qualifier if (type == GL_FRAGMENT_SHADER) code << "precision mediump float;\n"; -#if defined(USE_GLES2) - else if (type == GL_VERTEX_SHADER) +#else + int range[2], precision; + glGetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_HIGH_FLOAT, range, &precision); + + if (precision > 0) + code << "precision highp float;\n"; + else code << "precision mediump float;\n"; #endif From e1af0cc35357f22f681e0276b18543a0324ce7dd Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 9 Sep 2016 13:36:49 +1000 Subject: [PATCH 159/350] Fixed various bugs in max speed state saving. --- src/karts/max_speed.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/karts/max_speed.cpp b/src/karts/max_speed.cpp index 54bcc345e..9ab14fa50 100644 --- a/src/karts/max_speed.cpp +++ b/src/karts/max_speed.cpp @@ -178,6 +178,7 @@ void MaxSpeed::SpeedIncrease::rewindTo(BareNetworkString *buffer, { m_max_add_speed = buffer->getFloat(); m_duration = buffer->getFloat(); + m_fade_out_time = buffer->getFloat(); m_current_speedup = buffer->getFloat(); m_engine_force = buffer->getFloat(); } @@ -334,11 +335,10 @@ void MaxSpeed::saveState(BareNetworkString *buffer) const // ------------------------ // Get the bit pattern of all active slowdowns uint8_t active_slowdown = 0; - for(unsigned int i=MS_DECREASE_MIN; iaddUInt8(active_slowdown); @@ -352,10 +352,10 @@ void MaxSpeed::saveState(BareNetworkString *buffer) const // -------------------------- // Get the bit pattern of all active speedups uint8_t active_speedups = 0; - for(unsigned int i=MS_INCREASE_MIN; iaddUInt8(active_speedups); for(unsigned int i=MS_INCREASE_MIN, b=1; i Date: Fri, 9 Sep 2016 13:38:39 +1000 Subject: [PATCH 160/350] Added state saving for skidding. --- src/karts/kart_rewinder.cpp | 9 ++++++ src/karts/skidding.cpp | 64 ++++++++++++++++++++++++++++--------- src/karts/skidding.hpp | 5 ++- 3 files changed, 62 insertions(+), 16 deletions(-) diff --git a/src/karts/kart_rewinder.cpp b/src/karts/kart_rewinder.cpp index 03309bc97..65a863ae8 100644 --- a/src/karts/kart_rewinder.cpp +++ b/src/karts/kart_rewinder.cpp @@ -22,6 +22,7 @@ #include "items/powerup.hpp" #include "karts/abstract_kart.hpp" #include "karts/max_speed.hpp" +#include "karts/skidding.hpp" #include "modes/world.hpp" #include "network/rewind_manager.hpp" #include "network/network_string.hpp" @@ -89,6 +90,10 @@ BareNetworkString* KartRewinder::saveState() const // ------------------ m_max_speed->saveState(buffer); + // 6) Skidding + // ----------- + m_skidding->saveState(buffer); + return buffer; } // saveState @@ -124,6 +129,10 @@ void KartRewinder::rewindToState(BareNetworkString *buffer) // 5) Max speed info // ------------------ m_max_speed->rewindTo(buffer); + + // 6) Skidding + // ----------- + m_skidding->rewindTo(buffer); return; } // rewindToState diff --git a/src/karts/skidding.cpp b/src/karts/skidding.cpp index 3ed348428..365766902 100644 --- a/src/karts/skidding.cpp +++ b/src/karts/skidding.cpp @@ -29,6 +29,7 @@ #include "karts/max_speed.hpp" #include "karts/controller/controller.hpp" #include "modes/world.hpp" +#include "network/network_string.hpp" #include "physics/btKart.hpp" #include "tracks/track.hpp" #include "utils/log.hpp" @@ -85,6 +86,36 @@ void Skidding::reset() m_kart->getVehicle()->setTimedRotation(0, rot); } // reset +// ---------------------------------------------------------------------------- +/** Save the skidding state of a kart. It only saves the important physics + * values, not visual only values like m_visual_rotation, m_gfx_jump_offset, + * m_remaining_jump_time and m_jump_speed. Similarly m_real_steering is output + * of updateRewind() and will be recomputed every frame when update() is called, + * and similart m_skid_bonus_ready + * \param buffer Buffer for the state information. + */ +void Skidding::saveState(BareNetworkString *buffer) +{ + buffer->addUInt8(m_skid_state); + if(m_skid_state == SKID_NONE) + return; + buffer->addFloat(m_skid_time); + buffer->addFloat(m_skid_factor); +} // saveState + +// ---------------------------------------------------------------------------- +/** Restores the skidding state of a kart. + * \param buffer Buffer with state information. + */ +void Skidding::rewindTo(BareNetworkString *buffer) +{ + m_skid_state = (SkidState)buffer->getUInt8(); + if(m_skid_state == SKID_NONE) + return; + m_skid_time = buffer->getFloat(); + m_skid_factor = buffer->getFloat(); +} // rewindTo + // ---------------------------------------------------------------------------- /** Computes the actual steering fraction to be used in the physics, and * stores it in m_real_skidding. This is later used by kart to set the @@ -92,8 +123,10 @@ void Skidding::reset() * kart skids either left or right, the steering fraction is bound by * reduce-turn-min and reduce-turn-max. */ -void Skidding::updateSteering(float steer, float dt) +float Skidding::updateSteering(float steer, float dt) { + float steer_result; + const KartProperties *kp = m_kart->getKartProperties(); switch(m_skid_state) @@ -101,7 +134,7 @@ void Skidding::updateSteering(float steer, float dt) case SKID_SHOW_GFX_LEFT: case SKID_SHOW_GFX_RIGHT: case SKID_NONE: - m_real_steering = steer; + steer_result = steer; if (m_skid_time < kp->getSkidVisualTime() && m_skid_time > 0) { float f = m_visual_rotation - m_visual_rotation*dt/m_skid_time; @@ -115,7 +148,7 @@ void Skidding::updateSteering(float steer, float dt) } break; case SKID_BREAK: - m_real_steering = steer; + steer_result = steer; if (m_visual_rotation > 0.1f) m_visual_rotation -= 0.1f; else if (m_visual_rotation < -0.1f) m_visual_rotation += 0.1f; else @@ -126,33 +159,33 @@ void Skidding::updateSteering(float steer, float dt) case SKID_ACCUMULATE_RIGHT: { float f = (1.0f+steer)*0.5f; // map [-1,1] --> [0, 1] - m_real_steering = kp->getSkidReduceTurnMin() + steer_result = kp->getSkidReduceTurnMin() + m_skid_reduce_turn_delta * f; if(m_skid_time < kp->getSkidVisualTime()) m_visual_rotation = kp->getSkidVisual() - * m_real_steering * m_skid_time + * steer_result * m_skid_time / kp->getSkidVisualTime(); else - m_visual_rotation = kp->getSkidVisual() * m_real_steering; + m_visual_rotation = kp->getSkidVisual() * steer_result; break; - } + } // SKID_ACCUMULATE_RIGHT case SKID_ACCUMULATE_LEFT: { float f = (-1.0f+steer)*0.5f; // map [-1,1] --> [-1, 0] - m_real_steering = -kp->getSkidReduceTurnMin() + steer_result = -kp->getSkidReduceTurnMin() + m_skid_reduce_turn_delta * f; if(m_skid_time < kp->getSkidVisualTime()) m_visual_rotation = kp->getSkidVisual() - * m_real_steering * m_skid_time + * steer_result * m_skid_time / kp->getSkidVisualTime(); else - m_visual_rotation = kp->getSkidVisual() * m_real_steering; + m_visual_rotation = kp->getSkidVisual() * steer_result; break; - } - + } // case SKID_ACCUMULATE_LEFT } // switch m_skid_state + return steer_result; } // updateSteering // ---------------------------------------------------------------------------- @@ -279,7 +312,7 @@ void Skidding::update(float dt, bool is_on_ground, // a potential bonus. Also the rotation of the physical body to // be in synch with the graphical kart is started (which is // independently handled in the kart physics). - // SKID_SHOW_GFX_{LEFTclear(); m_actual_curve->setVisible(true); m_predicted_curve->clear(); @@ -444,7 +477,8 @@ void Skidding::update(float dt, bool is_on_ground, m_skid_state = SKID_NONE; } } // switch - updateSteering(steering, dt); + + m_real_steering = updateSteering(steering, dt); } // update // ---------------------------------------------------------------------------- diff --git a/src/karts/skidding.hpp b/src/karts/skidding.hpp index 4d4d854d8..4d50b05f4 100644 --- a/src/karts/skidding.hpp +++ b/src/karts/skidding.hpp @@ -23,6 +23,7 @@ #include "utils/leak_check.hpp" #include "utils/no_copy.hpp" +class BareNetworkString; class Kart; class ShowCurve; @@ -98,13 +99,15 @@ private: unsigned int getSkidBonus(float *bonus_time, float *bonus_speed, float *bonus_force) const; - void updateSteering(float steer, float dt); + float updateSteering(float steer, float dt); public: Skidding(Kart *kart); ~Skidding(); void reset(); void update(float dt, bool is_on_ground, float steer, KartControl::SkidControl skidding); + void saveState(BareNetworkString *buffer); + void rewindTo(BareNetworkString *buffer); // ------------------------------------------------------------------------ /** Determines how much the graphics model of the kart should be rotated * additionally (for skidding), depending on how long the kart has been From 9d65e2047c83df9d2de06abb0230ab97468a56e6 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 9 Sep 2016 12:46:42 +0800 Subject: [PATCH 161/350] Fix heading (which got overwriten by merge) --- src/karts/moveable.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/karts/moveable.cpp b/src/karts/moveable.cpp index f3ad9e05e..233d1e3eb 100644 --- a/src/karts/moveable.cpp +++ b/src/karts/moveable.cpp @@ -140,8 +140,8 @@ void Moveable::update(float dt) */ void Moveable::updatePosition() { - Vec3 forw_vec = m_transform.getBasis().getColumn(0); - m_heading = -atan2f(forw_vec.getZ(), forw_vec.getX()); + Vec3 forw_vec = m_transform.getBasis().getColumn(2); + m_heading = atan2f(forw_vec.getX(), forw_vec.getZ()); // The pitch in hpr is in between -pi and pi. But for the camera it // must be restricted to -pi/2 and pi/2 - so recompute it by restricting From 505b50da0c18e93a28024c61411edc8c6e5f9bed Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 9 Sep 2016 16:14:28 +0800 Subject: [PATCH 162/350] Make getNode return pointer. (easier with dynamic_cast later) --- src/items/bowling.cpp | 2 +- src/items/item.cpp | 4 +- src/items/item_manager.cpp | 2 +- src/items/rubber_ball.cpp | 14 ++-- src/karts/controller/end_controller.cpp | 10 +-- src/karts/controller/skidding_ai.cpp | 90 ++++++++++++------------- src/karts/controller/test_ai.cpp | 86 +++++++++++------------ src/karts/kart.cpp | 12 ++-- src/modes/linear_world.cpp | 8 +-- src/scriptengine/script_utils.cpp | 2 +- src/tracks/graph_node.cpp | 18 ++--- src/tracks/graph_node.hpp | 4 +- src/tracks/graph_structure.cpp | 2 +- src/tracks/node_2d.cpp | 4 +- src/tracks/node_2d.hpp | 4 +- src/tracks/node_3d.cpp | 4 +- src/tracks/node_3d.hpp | 4 +- src/tracks/quad_graph.cpp | 52 +++++++------- src/tracks/quad_graph.hpp | 5 +- src/tracks/track.cpp | 21 ++++-- src/tracks/track.hpp | 6 -- src/tracks/track_sector.cpp | 10 +-- 22 files changed, 185 insertions(+), 179 deletions(-) diff --git a/src/items/bowling.cpp b/src/items/bowling.cpp index dd2971fcf..41b95b9e4 100644 --- a/src/items/bowling.cpp +++ b/src/items/bowling.cpp @@ -64,7 +64,7 @@ Bowling::Bowling(AbstractKart *kart) { unsigned int sector = ((LinearWorld*)World::getWorld())-> getTrackSector(kart->getWorldKartId()).getCurrentGraphNode(); - quadNormal = QuadGraph::get()->getNode(sector).getNormal(); + quadNormal = QuadGraph::get()->getNode(sector)->getNormal(); } else quadNormal = btVector3(.0f, 1.0f, .0f); diff --git a/src/items/item.cpp b/src/items/item.cpp index d1dd8ca0e..9b6135f68 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -161,8 +161,8 @@ void Item::initItem(ItemType type, const Vec3 &xyz) Vec3 distances; QuadGraph::get()->spatialToTrack(&distances, m_xyz, m_graph_node); m_distance_from_center = distances.getX(); - const GraphNode &gn = QuadGraph::get()->getNode(m_graph_node); - const Vec3 right = gn.getRightUnitVector(); + const GraphNode* gn = QuadGraph::get()->getNode(m_graph_node); + const Vec3& right = gn->getRightUnitVector(); // Give it 10% more space, since the kart will not always come // parallel to the drive line. Vec3 delta = right * sqrt(m_distance_2) * 1.3f; diff --git a/src/items/item_manager.cpp b/src/items/item_manager.cpp index 6d72e8d87..ab746ef74 100644 --- a/src/items/item_manager.cpp +++ b/src/items/item_manager.cpp @@ -228,7 +228,7 @@ void ItemManager::insertItem(Item *item) // If the item is on the driveline, store it at the appropriate index if(graph_node > -1) { - int sector = QuadGraph::get()->getNode(graph_node).getNodeIndex(); + int sector = QuadGraph::get()->getNode(graph_node)->getNodeIndex(); (*m_items_in_quads)[sector].push_back(item); } else // otherwise store it in the 'outside' index diff --git a/src/items/rubber_ball.cpp b/src/items/rubber_ball.cpp index 961c2fbfe..d322964c7 100644 --- a/src/items/rubber_ball.cpp +++ b/src/items/rubber_ball.cpp @@ -100,7 +100,7 @@ RubberBall::RubberBall(AbstractKart *kart) // initialises the current graph node TrackSector::update(getXYZ()); const Vec3& normal = - QuadGraph::get()->getNode(getCurrentGraphNode()).getNormal(); + QuadGraph::get()->getNode(getCurrentGraphNode())->getNormal(); TerrainInfo::update(getXYZ(), -normal); initializeControlPoints(m_owner->getXYZ()); @@ -136,7 +136,7 @@ void RubberBall::initializeControlPoints(const Vec3 &xyz) // left or right when firing the ball off track. getNextControlPoint(); m_control_points[2] = - QuadGraph::get()->getNode(m_last_aimed_graph_node).getCenter(); + QuadGraph::get()->getNode(m_last_aimed_graph_node)->getCenter(); // This updates m_last_aimed_graph_node, and sets m_control_points[3] getNextControlPoint(); @@ -201,12 +201,12 @@ unsigned int RubberBall::getSuccessorToHitTarget(unsigned int node_index, unsigned int sect = lin_world->getSectorForKart(m_target); - succ = QuadGraph::get()->getNode(node_index).getSuccessorToReach(sect); + succ = QuadGraph::get()->getNode(node_index)->getSuccessorToReach(sect); if(dist) *dist += QuadGraph::get()->getNode(node_index) - .getDistanceToSuccessor(succ); - return QuadGraph::get()->getNode(node_index).getSuccessor(succ); + ->getDistanceToSuccessor(succ); + return QuadGraph::get()->getNode(node_index)->getSuccessor(succ); } // getSuccessorToHitTarget // ---------------------------------------------------------------------------- @@ -236,9 +236,9 @@ void RubberBall::getNextControlPoint() m_last_aimed_graph_node = next; m_length_cp_2_3 = dist; - const Quad &quad = + const GraphNode* gn = QuadGraph::get()->getNode(m_last_aimed_graph_node); - m_control_points[3] = quad.getCenter(); + m_control_points[3] = gn->getCenter(); } // getNextControlPoint // ---------------------------------------------------------------------------- diff --git a/src/karts/controller/end_controller.cpp b/src/karts/controller/end_controller.cpp index 5881c23a8..d1188e0a1 100644 --- a/src/karts/controller/end_controller.cpp +++ b/src/karts/controller/end_controller.cpp @@ -212,10 +212,10 @@ void EndController::handleSteering(float dt) */ //Reaction to being outside of the road if( fabsf(m_world->getDistanceToCenterForKart( m_kart->getWorldKartId() )) > - 0.5f* QuadGraph::get()->getNode(m_track_node).getPathWidth()+0.5f ) + 0.5f* QuadGraph::get()->getNode(m_track_node)->getPathWidth()+0.5f ) { const int next = m_next_node_index[m_track_node]; - target_point = QuadGraph::get()->getNode(next).getCenter(); + target_point = QuadGraph::get()->getNode(next)->getCenter(); #ifdef AI_DEBUG Log::debug("end_controller.cpp", "- Outside of road: steer to center point."); #endif @@ -275,7 +275,7 @@ void EndController::findNonCrashingPoint(Vec3 *result) target_sector = m_next_node_index[sector]; //direction is a vector from our kart to the sectors we are testing - direction = QuadGraph::get()->getNode(target_sector).getCenter() + direction = QuadGraph::get()->getNode(target_sector)->getCenter() - m_kart->getXYZ(); float len=direction.length_2d(); @@ -300,9 +300,9 @@ void EndController::findNonCrashingPoint(Vec3 *result) //If we are outside, the previous sector is what we are looking for if ( distance + m_kart_width * 0.5f - > QuadGraph::get()->getNode(sector).getPathWidth()*0.5f ) + > QuadGraph::get()->getNode(sector)->getPathWidth()*0.5f ) { - *result = QuadGraph::get()->getNode(sector).getCenter(); + *result = QuadGraph::get()->getNode(sector)->getCenter(); return; } } diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index 49ec74084..703995587 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -476,14 +476,14 @@ void SkiddingAI::handleSteering(float dt) m_world->getDistanceToCenterForKart( m_kart->getWorldKartId() ); if( fabsf(side_dist) > - 0.5f* QuadGraph::get()->getNode(m_track_node).getPathWidth()+0.5f ) + 0.5f* QuadGraph::get()->getNode(m_track_node)->getPathWidth()+0.5f ) { steer_angle = steerToPoint(QuadGraph::get()->getNode(next) - .getCenter()); + ->getCenter()); #ifdef AI_DEBUG m_debug_sphere[0]->setPosition(QuadGraph::get()->getNode(next) - .getCenter().toIrrVector()); + ->getCenter().toIrrVector()); Log::debug(getControllerName().c_str(), "Outside of road: steer to center point."); #endif @@ -636,7 +636,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, const float max_item_lookahead_distance = 30.f; while(distance < max_item_lookahead_distance) { - int n_index= QuadGraph::get()->getNode(node).getNodeIndex(); + int n_index= QuadGraph::get()->getNode(node)->getNodeIndex(); const std::vector &items_ahead = ItemManager::get()->getItemsInQuads(n_index); for(unsigned int i=0; igetXYZ(); Vec3 item_direction = xyz - m_kart->getXYZ(); Vec3 plane_normal = QuadGraph::get()->getNode(m_track_node) - .getNormal(); + ->getNormal(); float dist_to_plane = item_direction.dot(plane_normal); Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; @@ -865,7 +865,7 @@ bool SkiddingAI::handleSelectedItem(Vec3 kart_aim_direction, Vec3 *aim_point) // the kart is. The current quad provides a good estimate of the kart's plane. const Vec3 &xyz = m_item_to_collect->getXYZ(); Vec3 item_direction = xyz - m_kart->getXYZ(); - Vec3 plane_normal = QuadGraph::get()->getNode(m_track_node).getNormal(); + Vec3 plane_normal = QuadGraph::get()->getNode(m_track_node)->getNormal(); float dist_to_plane = item_direction.dot(plane_normal); Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; @@ -926,7 +926,7 @@ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, // rightmost point - if so, nothing to do. Vec3 left(items_to_avoid[index_left_most]->getXYZ()); int node_index = items_to_avoid[index_left_most]->getGraphNode(); - Vec3 normal = QuadGraph::get()->getNode(node_index).getNormal(); + Vec3 normal = QuadGraph::get()->getNode(node_index)->getNormal(); Vec3 p1 = line_to_target.start, p2 = line_to_target.getMiddle() + normal.toIrrVector(), p3 = line_to_target.end; @@ -946,7 +946,7 @@ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, { Vec3 left(items_to_avoid[index_left_most]->getXYZ()); int node_index = items_to_avoid[index_left_most]->getGraphNode(); - Vec3 normal = QuadGraph::get()->getNode(node_index).getNormal(); + Vec3 normal = QuadGraph::get()->getNode(node_index)->getNormal(); Vec3 p1 = line_to_target.start, p2 = line_to_target.getMiddle() + normal.toIrrVector(), p3 = line_to_target.end; @@ -1104,7 +1104,7 @@ void SkiddingAI::evaluateItems(const Item *item, Vec3 kart_aim_direction, // the kart is. The current quad provides a good estimate of the kart's plane. const Vec3 &xyz = item->getXYZ(); Vec3 item_direction = xyz - m_kart->getXYZ(); - Vec3 plane_normal = QuadGraph::get()->getNode(m_track_node).getNormal(); + Vec3 plane_normal = QuadGraph::get()->getNode(m_track_node)->getNormal(); float dist_to_plane = item_direction.dot(plane_normal); Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; @@ -1712,8 +1712,8 @@ void SkiddingAI::handleNitroAndZipper() { GraphNode::DirectionType dir; unsigned int last; - const GraphNode &gn = QuadGraph::get()->getNode(m_track_node); - gn.getDirectionData(m_successor_index[m_track_node], &dir, &last); + const GraphNode* gn = QuadGraph::get()->getNode(m_track_node); + gn->getDirectionData(m_successor_index[m_track_node], &dir, &last); if(dir==GraphNode::DIR_STRAIGHT) { float diff = QuadGraph::get()->getDistanceFromStart(last) @@ -1861,23 +1861,23 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node) *last_node = m_next_node_index[m_track_node]; const core::vector2df xz = m_kart->getXYZ().toIrrVector2d(); - const GraphNode &g = QuadGraph::get()->getNode(*last_node); + const GraphNode* g = QuadGraph::get()->getNode(*last_node); // Index of the left and right end of a quad. const unsigned int LEFT_END_POINT = 0; const unsigned int RIGHT_END_POINT = 1; - core::line2df left (xz, g[LEFT_END_POINT ].toIrrVector2d()); - core::line2df right(xz, g[RIGHT_END_POINT].toIrrVector2d()); + core::line2df left (xz, (*g)[LEFT_END_POINT ].toIrrVector2d()); + core::line2df right(xz, (*g)[RIGHT_END_POINT].toIrrVector2d()); #if defined(AI_DEBUG) && defined(AI_DEBUG_NEW_FIND_NON_CRASHING) const Vec3 eps1(0,0.5f,0); m_curve[CURVE_LEFT]->clear(); m_curve[CURVE_LEFT]->addPoint(m_kart->getXYZ()+eps1); - m_curve[CURVE_LEFT]->addPoint(g[LEFT_END_POINT]+eps1); + m_curve[CURVE_LEFT]->addPoint((*g)[LEFT_END_POINT]+eps1); m_curve[CURVE_LEFT]->addPoint(m_kart->getXYZ()+eps1); m_curve[CURVE_RIGHT]->clear(); m_curve[CURVE_RIGHT]->addPoint(m_kart->getXYZ()+eps1); - m_curve[CURVE_RIGHT]->addPoint(g[RIGHT_END_POINT]+eps1); + m_curve[CURVE_RIGHT]->addPoint((*g)[RIGHT_END_POINT]+eps1); m_curve[CURVE_RIGHT]->addPoint(m_kart->getXYZ()+eps1); #endif #if defined(AI_DEBUG_KART_HEADING) || defined(AI_DEBUG_NEW_FIND_NON_CRASHING) @@ -1890,13 +1890,13 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node) while(1) { unsigned int next_sector = m_next_node_index[*last_node]; - const GraphNode &g_next = QuadGraph::get()->getNode(next_sector); + const GraphNode* g_next = QuadGraph::get()->getNode(next_sector); // Test if the next left point is to the right of the left // line. If so, a new left line is defined. - if(left.getPointOrientation(g_next[LEFT_END_POINT].toIrrVector2d()) + if(left.getPointOrientation((*g_next)[LEFT_END_POINT].toIrrVector2d()) < 0 ) { - core::vector2df p = g_next[LEFT_END_POINT].toIrrVector2d(); + core::vector2df p = (*g_next)[LEFT_END_POINT].toIrrVector2d(); // Stop if the new point is to the right of the right line if(right.getPointOrientation(p)<0) break; @@ -1912,10 +1912,10 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node) // Test if new right point is to the left of the right line. If // so, a new right line is defined. - if(right.getPointOrientation(g_next[RIGHT_END_POINT].toIrrVector2d()) + if(right.getPointOrientation((*g_next)[RIGHT_END_POINT].toIrrVector2d()) > 0 ) { - core::vector2df p = g_next[RIGHT_END_POINT].toIrrVector2d(); + core::vector2df p = (*g_next)[RIGHT_END_POINT].toIrrVector2d(); // Break if new point is to the left of left line if(left.getPointOrientation(p)>0) break; @@ -1937,7 +1937,7 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node) // 0.5f*(left.end.Y+right.end.Y)); //*result = ppp; - *result = QuadGraph::get()->getNode(*last_node).getCenter(); + *result = QuadGraph::get()->getNode(*last_node)->getCenter(); } // findNonCrashingPointNew //----------------------------------------------------------------------------- @@ -1974,7 +1974,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) target_sector = m_next_node_index[*last_node]; //direction is a vector from our kart to the sectors we are testing - direction = QuadGraph::get()->getNode(target_sector).getCenter() + direction = QuadGraph::get()->getNode(target_sector)->getCenter() - m_kart->getXYZ(); float len=direction.length(); @@ -2004,16 +2004,16 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) //If we are outside, the previous node is what we are looking for if ( distance + m_kart_width * 0.5f - > QuadGraph::get()->getNode(*last_node).getPathWidth()*0.5f ) + > QuadGraph::get()->getNode(*last_node)->getPathWidth()*0.5f ) { *aim_position = QuadGraph::get()->getNode(*last_node) - .getCenter(); + ->getCenter(); return; } } *last_node = target_sector; } // for i<100 - *aim_position = QuadGraph::get()->getNode(*last_node).getCenter(); + *aim_position = QuadGraph::get()->getNode(*last_node)->getCenter(); } // findNonCrashingPointFixed //----------------------------------------------------------------------------- @@ -2021,7 +2021,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) * 1. the test: * * distance + m_kart_width * 0.5f - * > QuadGraph::get()->getNode(*last_node).getPathWidth() ) + * > QuadGraph::get()->getNode(*last_node)->getPathWidth() ) * * is incorrect, it should compare with getPathWith*0.5f (since distance * is the distance from the center, i.e. it is half the path width if @@ -2078,12 +2078,12 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) if(fabsf(diff)>1.5f) { *aim_position = QuadGraph::get()->getNode(target_sector) - .getCenter(); + ->getCenter(); return; } //direction is a vector from our kart to the sectors we are testing - direction = QuadGraph::get()->getNode(target_sector).getCenter() + direction = QuadGraph::get()->getNode(target_sector)->getCenter() - m_kart->getXYZ(); float len=direction.length(); @@ -2113,17 +2113,17 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) //If we are outside, the previous node is what we are looking for if ( distance + m_kart_width * 0.5f - > QuadGraph::get()->getNode(*last_node).getPathWidth() ) + > QuadGraph::get()->getNode(*last_node)->getPathWidth() ) { *aim_position = QuadGraph::get()->getNode(*last_node) - .getCenter(); + ->getCenter(); return; } } angle = angle1; *last_node = target_sector; } // for i<100 - *aim_position = QuadGraph::get()->getNode(*last_node).getCenter(); + *aim_position = QuadGraph::get()->getNode(*last_node)->getCenter(); } // findNonCrashingPoint //----------------------------------------------------------------------------- @@ -2134,13 +2134,13 @@ void SkiddingAI::determineTrackDirection() { const QuadGraph *qg = QuadGraph::get(); unsigned int succ = m_successor_index[m_track_node]; - unsigned int next = qg->getNode(m_track_node).getSuccessor(succ); + unsigned int next = qg->getNode(m_track_node)->getSuccessor(succ); - //float angle_to_track = qg->getNode(m_track_node).getAngleToSuccessor(succ) + //float angle_to_track = qg->getNode(m_track_node)->getAngleToSuccessor(succ) // - m_kart->getHeading(); - Vec3 track_direction = -qg->getNode(m_track_node).getCenter() - + qg->getNode(next).getCenter(); - //Vec3 kart_direction = qg->getNode(m_track_node).getCenter() + m_kart->getVelocity(); + Vec3 track_direction = -qg->getNode(m_track_node)->getCenter() + + qg->getNode(next)->getCenter(); + //Vec3 kart_direction = qg->getNode(m_track_node)->getCenter() + m_kart->getVelocity(); float angle_to_track = 0; if (m_kart->getVelocity().length() > 0.0f) @@ -2165,15 +2165,15 @@ void SkiddingAI::determineTrackDirection() return; } - qg->getNode(next).getDirectionData(m_successor_index[next], - &m_current_track_direction, - &m_last_direction_node); + qg->getNode(next)->getDirectionData(m_successor_index[next], + &m_current_track_direction, + &m_last_direction_node); #ifdef AI_DEBUG m_curve[CURVE_QG]->clear(); for(unsigned int i=m_track_node; i<=m_last_direction_node; i++) { - m_curve[CURVE_QG]->addPoint(qg->getNode(i).getCenter()); + m_curve[CURVE_QG]->addPoint(qg->getNode(i)->getCenter()); } #endif @@ -2206,7 +2206,7 @@ void SkiddingAI::handleCurve() const QuadGraph *qg = QuadGraph::get(); Vec3 xyz = m_kart->getXYZ(); Vec3 tangent = m_kart->getTrans()(Vec3(0,0,1)) - xyz; - Vec3 last_xyz = qg->getNode(m_last_direction_node).getCenter(); + Vec3 last_xyz = qg->getNode(m_last_direction_node)->getCenter(); determineTurnRadius(xyz, tangent, last_xyz, &m_curve_center, &m_current_curve_radius); @@ -2225,10 +2225,10 @@ void SkiddingAI::handleCurve() // Pick either the lower left or right point: int index = m_current_track_direction==GraphNode::DIR_LEFT ? 0 : 1; - float r = (m_curve_center - qg->getNode(i)[index]).length(); + float r = (m_curve_center - *(qg->getNode(i))[index]).length(); if(m_current_curve_radius < r) { - last_xyz = qg->getNode(i)[index]; + last_xyz = *(qg->getNode(i)[index]); determineTurnRadius(xyz, tangent, last_xyz, &m_curve_center, &m_current_curve_radius); m_last_direction_node = i; @@ -2298,7 +2298,7 @@ bool SkiddingAI::canSkid(float steer_fraction) const float MIN_SKID_SPEED = 5.0f; const QuadGraph *qg = QuadGraph::get(); - Vec3 last_xyz = qg->getNode(m_last_direction_node).getCenter(); + Vec3 last_xyz = qg->getNode(m_last_direction_node)->getCenter(); // Only try skidding when a certain minimum speed is reached. if(m_kart->getSpeed()getDistanceToCenterForKart( m_kart->getWorldKartId() ); if( fabsf(side_dist) > - 0.5f* QuadGraph::get()->getNode(m_track_node).getPathWidth()+0.5f ) + 0.5f* QuadGraph::get()->getNode(m_track_node)->getPathWidth()+0.5f ) { steer_angle = steerToPoint(QuadGraph::get()->getNode(next) - .getCenter()); + ->getCenter()); #ifdef AI_DEBUG m_debug_sphere[0]->setPosition(QuadGraph::get()->getNode(next) - .getCenter().toIrrVector()); + ->getCenter().toIrrVector()); Log::debug(getControllerName().c_str(), "Outside of road: steer to center point."); #endif @@ -642,7 +642,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, const float max_item_lookahead_distance = 30.f; while(distance < max_item_lookahead_distance) { - int n_index= QuadGraph::get()->getNode(node).getNodeIndex(); + int n_index= QuadGraph::get()->getNode(node)->getNodeIndex(); const std::vector &items_ahead = ItemManager::get()->getItemsInQuads(n_index); for(unsigned int i=0; igetXYZ(); Vec3 item_direction = xyz - m_kart->getXYZ(); Vec3 plane_normal = QuadGraph::get()->getNode(m_track_node) - .getNormal(); + ->getNormal(); float dist_to_plane = item_direction.dot(plane_normal); Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; @@ -871,7 +871,7 @@ bool SkiddingAI::handleSelectedItem(Vec3 kart_aim_direction, Vec3 *aim_point) // the kart is. The current quad provides a good estimate of the kart's plane. const Vec3 &xyz = m_item_to_collect->getXYZ(); Vec3 item_direction = xyz - m_kart->getXYZ(); - Vec3 plane_normal = QuadGraph::get()->getNode(m_track_node).getNormal(); + Vec3 plane_normal = QuadGraph::get()->getNode(m_track_node)->getNormal(); float dist_to_plane = item_direction.dot(plane_normal); Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; @@ -932,7 +932,7 @@ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, // rightmost point - if so, nothing to do. Vec3 left(items_to_avoid[index_left_most]->getXYZ()); int node_index = items_to_avoid[index_left_most]->getGraphNode(); - Vec3 normal = QuadGraph::get()->getNode(node_index).getNormal(); + Vec3 normal = QuadGraph::get()->getNode(node_index)->getNormal(); Vec3 p1 = line_to_target.start, p2 = line_to_target.getMiddle() + normal.toIrrVector(), p3 = line_to_target.end; @@ -952,7 +952,7 @@ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, { Vec3 left(items_to_avoid[index_left_most]->getXYZ()); int node_index = items_to_avoid[index_left_most]->getGraphNode(); - Vec3 normal = QuadGraph::get()->getNode(node_index).getNormal(); + Vec3 normal = QuadGraph::get()->getNode(node_index)->getNormal(); Vec3 p1 = line_to_target.start, p2 = line_to_target.getMiddle() + normal.toIrrVector(), p3 = line_to_target.end; @@ -1110,7 +1110,7 @@ void SkiddingAI::evaluateItems(const Item *item, Vec3 kart_aim_direction, // the kart is. The current quad provides a good estimate of the kart's plane. const Vec3 &xyz = item->getXYZ(); Vec3 item_direction = xyz - m_kart->getXYZ(); - Vec3 plane_normal = QuadGraph::get()->getNode(m_track_node).getNormal(); + Vec3 plane_normal = QuadGraph::get()->getNode(m_track_node)->getNormal(); float dist_to_plane = item_direction.dot(plane_normal); Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; @@ -1761,8 +1761,8 @@ void SkiddingAI::handleNitroAndZipper() { GraphNode::DirectionType dir; unsigned int last; - const GraphNode &gn = QuadGraph::get()->getNode(m_track_node); - gn.getDirectionData(m_successor_index[m_track_node], &dir, &last); + const GraphNode* gn = QuadGraph::get()->getNode(m_track_node); + gn->getDirectionData(m_successor_index[m_track_node], &dir, &last); if(dir==GraphNode::DIR_STRAIGHT) { float diff = QuadGraph::get()->getDistanceFromStart(last) @@ -1910,23 +1910,23 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node) *last_node = m_next_node_index[m_track_node]; const core::vector2df xz = m_kart->getXYZ().toIrrVector2d(); - const GraphNode &g = QuadGraph::get()->getNode(*last_node); + const GraphNode* g = QuadGraph::get()->getNode(*last_node); // Index of the left and right end of a quad. const unsigned int LEFT_END_POINT = 0; const unsigned int RIGHT_END_POINT = 1; - core::line2df left (xz, g[LEFT_END_POINT ].toIrrVector2d()); - core::line2df right(xz, g[RIGHT_END_POINT].toIrrVector2d()); + core::line2df left (xz, (*g)[LEFT_END_POINT ].toIrrVector2d()); + core::line2df right(xz, (*g)[RIGHT_END_POINT].toIrrVector2d()); #if defined(AI_DEBUG) && defined(AI_DEBUG_NEW_FIND_NON_CRASHING) const Vec3 eps1(0,0.5f,0); m_curve[CURVE_LEFT]->clear(); m_curve[CURVE_LEFT]->addPoint(m_kart->getXYZ()+eps1); - m_curve[CURVE_LEFT]->addPoint(g[LEFT_END_POINT]+eps1); + m_curve[CURVE_LEFT]->addPoint((*g)[LEFT_END_POINT]+eps1); m_curve[CURVE_LEFT]->addPoint(m_kart->getXYZ()+eps1); m_curve[CURVE_RIGHT]->clear(); m_curve[CURVE_RIGHT]->addPoint(m_kart->getXYZ()+eps1); - m_curve[CURVE_RIGHT]->addPoint(g[RIGHT_END_POINT]+eps1); + m_curve[CURVE_RIGHT]->addPoint((*g)[RIGHT_END_POINT]+eps1); m_curve[CURVE_RIGHT]->addPoint(m_kart->getXYZ()+eps1); #endif #if defined(AI_DEBUG_KART_HEADING) || defined(AI_DEBUG_NEW_FIND_NON_CRASHING) @@ -1939,13 +1939,13 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node) while(1) { unsigned int next_sector = m_next_node_index[*last_node]; - const GraphNode &g_next = QuadGraph::get()->getNode(next_sector); + const GraphNode* g_next = QuadGraph::get()->getNode(next_sector); // Test if the next left point is to the right of the left // line. If so, a new left line is defined. - if(left.getPointOrientation(g_next[LEFT_END_POINT].toIrrVector2d()) + if(left.getPointOrientation((*g_next)[LEFT_END_POINT].toIrrVector2d()) < 0 ) { - core::vector2df p = g_next[LEFT_END_POINT].toIrrVector2d(); + core::vector2df p = (*g_next)[LEFT_END_POINT].toIrrVector2d(); // Stop if the new point is to the right of the right line if(right.getPointOrientation(p)<0) break; @@ -1961,10 +1961,10 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node) // Test if new right point is to the left of the right line. If // so, a new right line is defined. - if(right.getPointOrientation(g_next[RIGHT_END_POINT].toIrrVector2d()) + if(right.getPointOrientation((*g_next)[RIGHT_END_POINT].toIrrVector2d()) > 0 ) { - core::vector2df p = g_next[RIGHT_END_POINT].toIrrVector2d(); + core::vector2df p = (*g_next)[RIGHT_END_POINT].toIrrVector2d(); // Break if new point is to the left of left line if(left.getPointOrientation(p)>0) break; @@ -1986,7 +1986,7 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node) // 0.5f*(left.end.Y+right.end.Y)); //*result = ppp; - *result = QuadGraph::get()->getNode(*last_node).getCenter(); + *result = QuadGraph::get()->getNode(*last_node)->getCenter(); } // findNonCrashingPointNew //----------------------------------------------------------------------------- @@ -2023,7 +2023,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) target_sector = m_next_node_index[*last_node]; //direction is a vector from our kart to the sectors we are testing - direction = QuadGraph::get()->getNode(target_sector).getCenter() + direction = QuadGraph::get()->getNode(target_sector)->getCenter() - m_kart->getXYZ(); float len=direction.length(); @@ -2053,16 +2053,16 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) //If we are outside, the previous node is what we are looking for if ( distance + m_kart_width * 0.5f - > QuadGraph::get()->getNode(*last_node).getPathWidth()*0.5f ) + > QuadGraph::get()->getNode(*last_node)->getPathWidth()*0.5f ) { *aim_position = QuadGraph::get()->getNode(*last_node) - .getCenter(); + ->getCenter(); return; } } *last_node = target_sector; } // for i<100 - *aim_position = QuadGraph::get()->getNode(*last_node).getCenter(); + *aim_position = QuadGraph::get()->getNode(*last_node)->getCenter(); } // findNonCrashingPointFixed //----------------------------------------------------------------------------- @@ -2070,7 +2070,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) * 1. the test: * * distance + m_kart_width * 0.5f - * > QuadGraph::get()->getNode(*last_node).getPathWidth() ) + * > QuadGraph::get()->getNode(*last_node)->getPathWidth() ) * * is incorrect, it should compare with getPathWith*0.5f (since distance * is the distance from the center, i.e. it is half the path width if @@ -2127,12 +2127,12 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) if(fabsf(diff)>1.5f) { *aim_position = QuadGraph::get()->getNode(target_sector) - .getCenter(); + ->getCenter(); return; } //direction is a vector from our kart to the sectors we are testing - direction = QuadGraph::get()->getNode(target_sector).getCenter() + direction = QuadGraph::get()->getNode(target_sector)->getCenter() - m_kart->getXYZ(); float len=direction.length(); @@ -2162,17 +2162,17 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) //If we are outside, the previous node is what we are looking for if ( distance + m_kart_width * 0.5f - > QuadGraph::get()->getNode(*last_node).getPathWidth() ) + > QuadGraph::get()->getNode(*last_node)->getPathWidth() ) { *aim_position = QuadGraph::get()->getNode(*last_node) - .getCenter(); + ->getCenter(); return; } } angle = angle1; *last_node = target_sector; } // for i<100 - *aim_position = QuadGraph::get()->getNode(*last_node).getCenter(); + *aim_position = QuadGraph::get()->getNode(*last_node)->getCenter(); } // findNonCrashingPoint //----------------------------------------------------------------------------- @@ -2183,13 +2183,13 @@ void SkiddingAI::determineTrackDirection() { const QuadGraph *qg = QuadGraph::get(); unsigned int succ = m_successor_index[m_track_node]; - unsigned int next = qg->getNode(m_track_node).getSuccessor(succ); + unsigned int next = qg->getNode(m_track_node)->getSuccessor(succ); - //float angle_to_track = qg->getNode(m_track_node).getAngleToSuccessor(succ) + //float angle_to_track = qg->getNode(m_track_node)->getAngleToSuccessor(succ) // - m_kart->getHeading(); - Vec3 track_direction = -qg->getNode(m_track_node).getCenter() - + qg->getNode(next).getCenter(); - //Vec3 kart_direction = qg->getNode(m_track_node).getCenter() + m_kart->getVelocity(); + Vec3 track_direction = -qg->getNode(m_track_node)->getCenter() + + qg->getNode(next)->getCenter(); + //Vec3 kart_direction = qg->getNode(m_track_node)->getCenter() + m_kart->getVelocity(); float angle_to_track = 0; if (m_kart->getVelocity().length() > 0.0f) @@ -2214,7 +2214,7 @@ void SkiddingAI::determineTrackDirection() return; } - qg->getNode(next).getDirectionData(m_successor_index[next], + qg->getNode(next)->getDirectionData(m_successor_index[next], &m_current_track_direction, &m_last_direction_node); @@ -2222,7 +2222,7 @@ void SkiddingAI::determineTrackDirection() m_curve[CURVE_QG]->clear(); for(unsigned int i=m_track_node; i<=m_last_direction_node; i++) { - m_curve[CURVE_QG]->addPoint(qg->getNode(i).getCenter()); + m_curve[CURVE_QG]->addPoint(qg->getNode(i)->getCenter()); } #endif @@ -2255,7 +2255,7 @@ void SkiddingAI::handleCurve() const QuadGraph *qg = QuadGraph::get(); Vec3 xyz = m_kart->getXYZ(); Vec3 tangent = m_kart->getTrans()(Vec3(0,0,1)) - xyz; - Vec3 last_xyz = qg->getNode(m_last_direction_node).getCenter(); + Vec3 last_xyz = qg->getNode(m_last_direction_node)->getCenter(); determineTurnRadius(xyz, tangent, last_xyz, &m_curve_center, &m_current_curve_radius); @@ -2274,10 +2274,10 @@ void SkiddingAI::handleCurve() // Pick either the lower left or right point: int index = m_current_track_direction==GraphNode::DIR_LEFT ? 0 : 1; - float r = (m_curve_center - qg->getNode(i)[index]).length(); + float r = (m_curve_center - *(qg->getNode(i))[index]).length(); if(m_current_curve_radius < r) { - last_xyz = qg->getNode(i)[index]; + last_xyz = *(qg->getNode(i))[index]; determineTurnRadius(xyz, tangent, last_xyz, &m_curve_center, &m_current_curve_radius); m_last_direction_node = i; @@ -2347,7 +2347,7 @@ bool SkiddingAI::canSkid(float steer_fraction) const float MIN_SKID_SPEED = 5.0f; const QuadGraph *qg = QuadGraph::get(); - Vec3 last_xyz = qg->getNode(m_last_direction_node).getCenter(); + Vec3 last_xyz = qg->getNode(m_last_direction_node)->getCenter(); // Only try skidding when a certain minimum speed is reached. if(m_kart->getSpeed()getTrackSector(getWorldKartId()).getCurrentGraphNode(); - quadNormal = QuadGraph::get()->getNode(sector).getNormal(); + quadNormal = QuadGraph::get()->getNode(sector)->getNormal(); btQuaternion q = getTrans().getRotation(); float roll = quadNormal.angle((Vec3(0, 1, 0).rotate(q.getAxis(), q.getAngle()))); @@ -1376,7 +1376,7 @@ void Kart::update(float dt) // We do this for now because dist_to_sector is not defined float dist_to_sector; if (QuadGraph::get()) - dist_to_sector = getXYZ().distance(QuadGraph::get()->getNode(sector).getCenter()); + dist_to_sector = getXYZ().distance(QuadGraph::get()->getNode(sector)->getCenter()); else dist_to_sector = 0; @@ -1944,9 +1944,9 @@ void Kart::crashed(const Material *m, const Vec3 &normal) { // Use the first predecessor node, which is the most // natural one (i.e. the one on the main driveline). - const GraphNode &gn = QuadGraph::get()->getNode( - QuadGraph::get()->getNode(sector).getPredecessor(0)); - Vec3 impulse = gn.getCenter() - getXYZ(); + const GraphNode* gn = QuadGraph::get()->getNode( + QuadGraph::get()->getNode(sector)->getPredecessor(0)); + Vec3 impulse = gn->getCenter() - getXYZ(); impulse.setY(0); if(impulse.getX() || impulse.getZ()) impulse.normalize(); @@ -2423,7 +2423,7 @@ void Kart::updateSliding() if (QuadGraph::get()) { int sector = ((LinearWorld*)World::getWorld())->getTrackSector(getWorldKartId()).getCurrentGraphNode(); - Vec3 quadNormal = QuadGraph::get()->getNode(sector).getNormal(); + Vec3 quadNormal = QuadGraph::get()->getNode(sector)->getNormal(); Vec3 kart_up = m.getColumn(1); distanceFromUp = kart_up.dot(quadNormal); } diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index ca72db1f0..ccefd0eb9 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -647,8 +647,8 @@ unsigned int LinearWorld::getRescuePositionIndex(AbstractKart *kart) // ------------------------------------------------------------------------ btTransform LinearWorld::getRescueTransform(unsigned int index) const { - const Vec3 &xyz = QuadGraph::get()->getNode(index).getCenter(); - const Vec3 &normal = QuadGraph::get()->getNode(index).getNormal(); + const Vec3 &xyz = QuadGraph::get()->getNode(index)->getCenter(); + const Vec3 &normal = QuadGraph::get()->getNode(index)->getNormal(); btTransform pos; pos.setOrigin(xyz); @@ -879,8 +879,8 @@ void LinearWorld::checkForWrongDirection(unsigned int i, float dt) return; // check if the player is going in the wrong direction - GraphNode& node = QuadGraph::get()->getNode(sector); - Vec3 center_line = node.getUpperCenter() - node.getLowerCenter(); + const GraphNode* node = QuadGraph::get()->getNode(sector); + Vec3 center_line = node->getUpperCenter() - node->getLowerCenter(); float angle_diff = kart->getVelocity().angle(center_line); if (angle_diff > M_PI) diff --git a/src/scriptengine/script_utils.cpp b/src/scriptengine/script_utils.cpp index be5c80d2e..18b3b3496 100644 --- a/src/scriptengine/script_utils.cpp +++ b/src/scriptengine/script_utils.cpp @@ -23,13 +23,13 @@ #include "input/input_device.hpp" #include "input/input_manager.hpp" #include "modes/world.hpp" +#include "scriptengine/script_engine.hpp" #include "states_screens/dialogs/tutorial_message_dialog.hpp" #include "tracks/track.hpp" #include "tracks/track_object.hpp" #include "tracks/track_object_manager.hpp" #include -#include "scriptarray.hpp" #include #include //debug diff --git a/src/tracks/graph_node.cpp b/src/tracks/graph_node.cpp index 7e838ad5d..e5d5efacd 100644 --- a/src/tracks/graph_node.cpp +++ b/src/tracks/graph_node.cpp @@ -72,17 +72,17 @@ void GraphNode::addSuccessor(unsigned int to) { m_successor_nodes.push_back(to); // to is the graph node - GraphNode &gn_to = QuadGraph::get()->getNode(to); + GraphNode* gn_to = QuadGraph::get()->getNode(to); // Note that the first predecessor is (because of the way the quad graph // is exported) the most 'natural' one, i.e. the one on the main // driveline. - gn_to.m_predecessor_nodes.push_back(m_node_index); + gn_to->m_predecessor_nodes.push_back(m_node_index); - Vec3 d = m_lower_center - gn_to.m_lower_center; + Vec3 d = m_lower_center - gn_to->m_lower_center; m_distance_to_next.push_back(d.length()); - Vec3 diff = gn_to.getCenter() - getCenter(); + Vec3 diff = gn_to->getCenter() - getCenter(); core::CMatrix4 m; m.buildRotateFromTo(getNormal().toIrrVector(), @@ -123,8 +123,8 @@ void GraphNode::setupPathsToNode() // not be used. for(unsigned int i=0; igetNode(getSuccessor(i)); - gn.markAllSuccessorsToUse(i, &m_path_to_node); + GraphNode* gn = QuadGraph::get()->getNode(getSuccessor(i)); + gn->markAllSuccessorsToUse(i, &m_path_to_node); } #ifdef DEBUG for(unsigned int i = 0; i < m_path_to_node.size(); ++i) @@ -154,8 +154,8 @@ void GraphNode::markAllSuccessorsToUse(unsigned int n, (*path_to_node)[m_node_index] = n; for(unsigned int i=0; igetNode(getSuccessor(i)); - gn.markAllSuccessorsToUse(n, path_to_node); + GraphNode* gn = QuadGraph::get()->getNode(getSuccessor(i)); + gn->markAllSuccessorsToUse(n, path_to_node); } } // markAllSuccesorsToUse @@ -185,5 +185,5 @@ void GraphNode::setChecklineRequirements(int latest_checkline) */ bool GraphNode::ignoreSuccessorForAI(unsigned int i) const { - return QuadGraph::get()->getNode(m_successor_nodes[i]).letAIIgnore(); + return QuadGraph::get()->getNode(m_successor_nodes[i])->letAIIgnore(); } // ignoreSuccessorForAI diff --git a/src/tracks/graph_node.hpp b/src/tracks/graph_node.hpp index c8850540e..3263fd2c0 100644 --- a/src/tracks/graph_node.hpp +++ b/src/tracks/graph_node.hpp @@ -210,9 +210,9 @@ public: /** True if this node should be ignored by the AI. */ bool letAIIgnore() const { return m_ai_ignore; } // ------------------------------------------------------------------------ - virtual float getDistance2FromPoint(const Vec3 &xyz) = 0; + virtual float getDistance2FromPoint(const Vec3 &xyz) const = 0; // ------------------------------------------------------------------------ - virtual void getDistances(const Vec3 &xyz, Vec3 *result) = 0; + virtual void getDistances(const Vec3 &xyz, Vec3 *result) const = 0; }; // GraphNode diff --git a/src/tracks/graph_structure.cpp b/src/tracks/graph_structure.cpp index e6fd0cd71..3dfe9aa28 100644 --- a/src/tracks/graph_structure.cpp +++ b/src/tracks/graph_structure.cpp @@ -168,7 +168,7 @@ void GraphStructure::createMesh(bool show_invisible, m_mesh_buffer->append(new_v, n*4, ind, n*6); - if (hasLapLine()) + if (0) { video::S3DVertex lap_v[4]; irr::u16 lap_ind[6]; diff --git a/src/tracks/node_2d.cpp b/src/tracks/node_2d.cpp index ad6dcba61..2f6b7c2f2 100644 --- a/src/tracks/node_2d.cpp +++ b/src/tracks/node_2d.cpp @@ -41,7 +41,7 @@ Node2D::Node2D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, * \param result The X coordinate contains the sidewards distance, the * Z coordinate the forward distance. */ -void Node2D::getDistances(const Vec3 &xyz, Vec3 *result) +void Node2D::getDistances(const Vec3 &xyz, Vec3 *result) const { core::vector2df xyz2d(xyz.getX(), xyz.getZ()); core::vector2df closest = m_line.getClosestPoint(xyz2d); @@ -61,7 +61,7 @@ void Node2D::getDistances(const Vec3 &xyz, Vec3 *result) * which belongs to this graph node. The value is computed in 2d only! * \param xyz The point for which the distance to the line is computed. */ -float Node2D::getDistance2FromPoint(const Vec3 &xyz) +float Node2D::getDistance2FromPoint(const Vec3 &xyz) const { core::vector2df xyz2d(xyz.getX(), xyz.getZ()); core::vector2df closest = m_line.getClosestPoint(xyz2d); diff --git a/src/tracks/node_2d.hpp b/src/tracks/node_2d.hpp index cb6bb3874..036e7cbf1 100644 --- a/src/tracks/node_2d.hpp +++ b/src/tracks/node_2d.hpp @@ -47,9 +47,9 @@ public: const Vec3 &normal, unsigned int node_index, bool invisible, bool ai_ignore); // ------------------------------------------------------------------------ - virtual void getDistances(const Vec3 &xyz, Vec3 *result) OVERRIDE; + virtual void getDistances(const Vec3 &xyz, Vec3 *result) const OVERRIDE; // ------------------------------------------------------------------------ - virtual float getDistance2FromPoint(const Vec3 &xyz) OVERRIDE; + virtual float getDistance2FromPoint(const Vec3 &xyz) const OVERRIDE; }; #endif diff --git a/src/tracks/node_3d.cpp b/src/tracks/node_3d.cpp index 9593e5666..a83a51c8f 100644 --- a/src/tracks/node_3d.cpp +++ b/src/tracks/node_3d.cpp @@ -66,7 +66,7 @@ Node3D::Node3D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, * \param result The X coordinate contains the sidewards distance, the * Z coordinate the forward distance. */ -void Node3D::getDistances(const Vec3 &xyz, Vec3 *result) +void Node3D::getDistances(const Vec3 &xyz, Vec3 *result) const { core::vector3df xyz_irr = xyz.toIrrVector(); core::vector3df closest = m_line.getClosestPoint(xyz.toIrrVector()); @@ -87,7 +87,7 @@ void Node3D::getDistances(const Vec3 &xyz, Vec3 *result) * which belongs to this node. * \param xyz The point for which the distance to the line is computed. */ -float Node3D::getDistance2FromPoint(const Vec3 &xyz) +float Node3D::getDistance2FromPoint(const Vec3 &xyz) const { core::vector3df closest = m_line.getClosestPoint(xyz.toIrrVector()); return (closest-xyz.toIrrVector()).getLengthSQ(); diff --git a/src/tracks/node_3d.hpp b/src/tracks/node_3d.hpp index bd395fabd..8da8f271b 100644 --- a/src/tracks/node_3d.hpp +++ b/src/tracks/node_3d.hpp @@ -56,9 +56,9 @@ public: return true; } // ------------------------------------------------------------------------ - virtual void getDistances(const Vec3 &xyz, Vec3 *result) OVERRIDE; + virtual void getDistances(const Vec3 &xyz, Vec3 *result) const OVERRIDE; // ------------------------------------------------------------------------ - virtual float getDistance2FromPoint(const Vec3 &xyz) OVERRIDE; + virtual float getDistance2FromPoint(const Vec3 &xyz) const OVERRIDE; }; #endif diff --git a/src/tracks/quad_graph.cpp b/src/tracks/quad_graph.cpp index f8d3c86ca..9c5b1d5c1 100644 --- a/src/tracks/quad_graph.cpp +++ b/src/tracks/quad_graph.cpp @@ -437,20 +437,20 @@ void QuadGraph::setDefaultStartPositions(AlignedArray else { // First find on which segment we have to start - while(distance_from_start > getNode(current_node).getNodeLength()) + while(distance_from_start > getNode(current_node)->getNodeLength()) { - distance_from_start -= getNode(current_node).getNodeLength(); + distance_from_start -= getNode(current_node)->getNodeLength(); // Only follow the main driveline, i.e. first predecessor - current_node = getNode(current_node).getPredecessor(0); + current_node = getNode(current_node)->getPredecessor(0); } - const GraphNode &gn = getNode(current_node); - Vec3 center_line = gn.getLowerCenter() - gn.getUpperCenter(); + const GraphNode* gn = getNode(current_node); + Vec3 center_line = gn->getLowerCenter() - gn->getUpperCenter(); center_line.normalize(); - Vec3 horizontal_line = gn[2] - gn[3]; + Vec3 horizontal_line = (*gn)[2] - (*gn)[3]; horizontal_line.normalize(); - Vec3 start = gn.getUpperCenter() + Vec3 start = gn->getUpperCenter() + center_line * distance_from_start + horizontal_line * x_pos; // Add a certain epsilon to the height in case that the @@ -458,7 +458,7 @@ void QuadGraph::setDefaultStartPositions(AlignedArray (*start_transforms)[i].setOrigin(start+Vec3(0,upwards_distance,0)); (*start_transforms)[i].setRotation( btQuaternion(btVector3(0, 1, 0), - gn.getAngleToSuccessor(0))); + gn->getAngleToSuccessor(0))); if(x_pos >= max_x_dist-sidewards_distance*0.5f) { x_pos = -max_x_dist; @@ -563,23 +563,23 @@ void QuadGraph::updateDistancesForAllSuccessors(unsigned int indx, float delta, } recursive_count++; - GraphNode &g=getNode(indx); - g.setDistanceFromStart(g.getDistanceFromStart()+delta); - for(unsigned int i=0; isetDistanceFromStart(g->getDistanceFromStart()+delta); + for(unsigned int i=0; igetNumberOfSuccessors(); i++) { - GraphNode &g_next = getNode(g.getSuccessor(i)); + GraphNode* g_next = getNode(g->getSuccessor(i)); // Stop when we reach the start node, i.e. the only node with a // distance of 0 - if(g_next.getDistanceFromStart()==0) + if(g_next->getDistanceFromStart()==0) continue; // Only increase the distance from start of a successor node, if // this successor has a distance from start that is smaller then // the increased amount. - if(g.getDistanceFromStart()+g.getDistanceToSuccessor(i) > - g_next.getDistanceFromStart()) + if(g->getDistanceFromStart()+g->getDistanceToSuccessor(i) > + g_next->getDistanceFromStart()) { - updateDistancesForAllSuccessors(g.getSuccessor(i), delta, + updateDistancesForAllSuccessors(g->getSuccessor(i), delta, recursive_count); } } @@ -648,14 +648,14 @@ void QuadGraph::determineDirection(unsigned int current, // Compute the angle from n (=current) to n+1 (=next) float angle_current = getAngleToNext(current, succ_index); - unsigned int next = getNode(current).getSuccessor(succ_index); + unsigned int next = getNode(current)->getSuccessor(succ_index); float angle_next = getAngleToNext(next, 0); float rel_angle = normalizeAngle(angle_next-angle_current); // Small angles are considered to be straight if(fabsf(rel_angle)getSuccessor(0); // next is now n+2 // If the direction is still the same during a lap the last node // in the same direction is the previous node; @@ -678,7 +678,7 @@ void QuadGraph::determineDirection(unsigned int current, break; rel_angle = new_rel_angle; - next = getNode(next).getSuccessor(0); + next = getNode(next)->getSuccessor(0); } // while(1) GraphNode::DirectionType dir = @@ -708,7 +708,7 @@ void QuadGraph::spatialToTrack(Vec3 *dst, const Vec3& xyz, return; } - getNode(sector).getDistances(xyz, dst); + getNode(sector)->getDistances(xyz, dst); } // spatialToTrack //----------------------------------------------------------------------------- @@ -728,7 +728,7 @@ void QuadGraph::findRoadSector(const Vec3& xyz, int *sector, { // Most likely the kart will still be on the sector it was before, // so this simple case is tested first. - if(*sector!=UNKNOWN_SECTOR && getNode(*sector).pointInside(xyz) ) + if(*sector!=UNKNOWN_SECTOR && getNode(*sector)->pointInside(xyz) ) { return; } // if still on same quad @@ -756,8 +756,8 @@ void QuadGraph::findRoadSector(const Vec3& xyz, int *sector, indx = (*all_sectors)[i]; else indx = indx<(int)m_all_nodes.size()-1 ? indx +1 : 0; - const GraphNode &gn = getNode(indx); - if(gn.pointInside(xyz)) + const GraphNode* gn = getNode(indx); + if(gn->pointInside(xyz)) { *sector = indx; } @@ -846,8 +846,8 @@ int QuadGraph::findOutOfRoadSector(const Vec3& xyz, float dist_2 = m_all_nodes[next_sector]->getDistance2FromPoint(xyz); if(dist_2getMinHeight(); // While negative distances are unlikely, we allow some small // negative numbers in case that the kart is partly in the // track. Only do the height test in phase==0, in phase==1 @@ -920,5 +920,5 @@ const bool QuadGraph::differentNodeColor(int n, NodeColor* c) const *c = COLOR_YELLOW; return true; } - return false; + return n == 0; } // differentNodeColor diff --git a/src/tracks/quad_graph.hpp b/src/tracks/quad_graph.hpp index a5ead4155..c93748b50 100644 --- a/src/tracks/quad_graph.hpp +++ b/src/tracks/quad_graph.hpp @@ -164,7 +164,10 @@ public: int getNumberOfSuccessors(int n) const; // ------------------------------------------------------------------------ /** Returns the quad that belongs to a graph node. */ - GraphNode& getNode(unsigned int j) const { return *m_all_nodes[j]; } + GraphNode* getNode(unsigned int j) { return m_all_nodes[j]; } + // ------------------------------------------------------------------------ + /** Returns the quad that belongs to a graph node (const version). */ + const GraphNode* getNode(unsigned int j) const { return m_all_nodes[j]; } // ------------------------------------------------------------------------ /** Returns the distance from the start to the beginning of a quad. */ float getDistanceFromStart(int j) const; diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index 0b215a1d0..2152819c8 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -52,13 +52,14 @@ #include "physics/physics.hpp" #include "physics/triangle_mesh.hpp" #include "race/race_manager.hpp" +#include "scriptengine/script_engine.hpp" #include "tracks/bezier_curve.hpp" #include "tracks/battle_graph.hpp" #include "tracks/check_manager.hpp" #include "tracks/graph_node.hpp" #include "tracks/model_definition_loader.hpp" -#include "tracks/track_manager.hpp" #include "tracks/quad_graph.hpp" +#include "tracks/track_manager.hpp" #include "tracks/track_object_manager.hpp" #include "utils/constants.hpp" #include "utils/log.hpp" @@ -698,7 +699,7 @@ void Track::loadQuadGraph(unsigned int mode_id, const bool reverse) #ifdef DEBUG for(unsigned int i=0; igetNumNodes(); i++) { - assert(QuadGraph::get()->getNode(i).getPredecessor(0)!=-1); + assert(QuadGraph::get()->getNode(i)->getPredecessor(0)!=-1); } #endif @@ -2292,7 +2293,7 @@ void Track::itemCommand(const XMLNode *node) // If a valid road_sector is not found if (road_sector == QuadGraph::UNKNOWN_SECTOR) road_sector = QuadGraph::get()->findOutOfRoadSector(xyz, road_sector); - const Vec3& quadnormal = QuadGraph::get()->getNode(road_sector).getNormal(); + const Vec3& quadnormal = QuadGraph::get()->getNode(road_sector)->getNormal(); const Material *m; Vec3 hit_point; @@ -2385,7 +2386,7 @@ bool Track::findGround(AbstractKart *kart) { int sector = ((LinearWorld*)World::getWorld())->getTrackSector(kart->getWorldKartId()).getCurrentGraphNode(); if (sector != QuadGraph::UNKNOWN_SECTOR) - quadNormal = QuadGraph::get()->getNode(sector).getNormal(); + quadNormal = QuadGraph::get()->getNode(sector)->getNormal(); } to = to + -1000.0f*quadNormal; @@ -2438,6 +2439,14 @@ bool Track::findGround(AbstractKart *kart) return true; } // findGround +//----------------------------------------------------------------------------- +float Track::getTrackLength() const +{ + return QuadGraph::get()->getLapLength(); +} // getTrackLength -float Track::getTrackLength() const {return QuadGraph::get()->getLapLength();} -float Track::getAngle(int n) const { return QuadGraph::get()->getAngleToNext(n, 0); } \ No newline at end of file +//----------------------------------------------------------------------------- +float Track::getAngle(int n) const +{ + return QuadGraph::get()->getAngleToNext(n, 0); +} // getAngle diff --git a/src/tracks/track.hpp b/src/tracks/track.hpp index f258ac997..4c51cf1fd 100644 --- a/src/tracks/track.hpp +++ b/src/tracks/track.hpp @@ -40,7 +40,6 @@ class ModelDefinitionLoader; #include "LinearMath/btTransform.h" -#include "scriptengine/script_engine.hpp" #include "utils/aligned_array.hpp" #include "utils/translation.hpp" #include "utils/vec3.hpp" @@ -62,11 +61,6 @@ class TriangleMesh; class World; class XMLNode; -namespace Scripting -{ - class ScriptEngine; -} - const int HEIGHT_MAP_RESOLUTION = 256; // TODO: eventually remove this and fully replace with scripting diff --git a/src/tracks/track_sector.cpp b/src/tracks/track_sector.cpp index a7a325e9d..6273b4999 100644 --- a/src/tracks/track_sector.cpp +++ b/src/tracks/track_sector.cpp @@ -65,8 +65,8 @@ void TrackSector::update(const Vec3 &xyz) { // keep the current quad as the latest valid one IF the player has one // of the required checklines - const GraphNode& gn = QuadGraph::get()->getNode(m_current_graph_node); - const std::vector& checkline_requirements = gn.getChecklineRequirements(); + const GraphNode* gn = QuadGraph::get()->getNode(m_current_graph_node); + const std::vector& checkline_requirements = gn->getChecklineRequirements(); if (checkline_requirements.size() == 0) { @@ -111,9 +111,9 @@ void TrackSector::rescue() // STK does not keep track of where the kart is coming from, so always // use the first predecessor, which is the one on the main driveline. m_current_graph_node = QuadGraph::get()->getNode(m_current_graph_node) - .getPredecessor(0); + ->getPredecessor(0); m_last_valid_graph_node = QuadGraph::get()->getNode(m_current_graph_node) - .getPredecessor(0); + ->getPredecessor(0); } // rescue // ---------------------------------------------------------------------------- @@ -123,7 +123,7 @@ void TrackSector::rescue() */ float TrackSector::getRelativeDistanceToCenter() const { - float w = QuadGraph::get()->getNode(m_current_graph_node).getPathWidth(); + float w = QuadGraph::get()->getNode(m_current_graph_node)->getPathWidth(); // w * 0.5 is the distance from center of the quad to the left or right // This way we get a value between -1 and 1. float ratio = getDistanceToCenter()/(w*0.5f); From ca4640716a05e606b16b5c73e0ab2c19d8004751 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 9 Sep 2016 16:30:35 +0800 Subject: [PATCH 163/350] Fix findOutOfRoadSector with Node3D --- src/tracks/quad_graph.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/tracks/quad_graph.cpp b/src/tracks/quad_graph.cpp index 9c5b1d5c1..d712c0ab9 100644 --- a/src/tracks/quad_graph.cpp +++ b/src/tracks/quad_graph.cpp @@ -847,12 +847,14 @@ int QuadGraph::findOutOfRoadSector(const Vec3& xyz, if(dist_2getMinHeight(); + const bool is_3d = (dynamic_cast(gn) != NULL); + float dist = xyz.getY() - gn->getMinHeight(); // While negative distances are unlikely, we allow some small // negative numbers in case that the kart is partly in the // track. Only do the height test in phase==0, in phase==1 - // accept any point, independent of height. - if(phase==1 || (dist < 5.0f && dist>-1.0f) ) + // accept any point, independent of height, or this node is 3d + // which already takes height into account + if(phase==1 || (dist < 5.0f && dist>-1.0f) || is_3d) { min_dist_2 = dist_2; min_sector = next_sector; From 4f675678348a3a9f72098958cf20d4fd66b498af Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 9 Sep 2016 19:48:11 +0800 Subject: [PATCH 164/350] Fix the up vector --- src/karts/kart.cpp | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 61e5a319c..75643c0e2 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1289,21 +1289,27 @@ void Kart::update(float dt) // But only do this if auto-rescue is enabled (i.e. it will be disabled in // battle mode), and the material the kart is driving on does not have // gravity (which atm affects the roll angle). - unsigned int sector = 0; - Vec3 quadNormal(0, 1, 0); + + // To be used later + float dist_to_sector = 0.0f; if (QuadGraph::get()) { - sector = ((LinearWorld*)World::getWorld())->getTrackSector(getWorldKartId()).getCurrentGraphNode(); - quadNormal = QuadGraph::get()->getNode(sector)->getNormal(); - btQuaternion q = getTrans().getRotation(); - float roll = quadNormal.angle((Vec3(0, 1, 0).rotate(q.getAxis(), q.getAngle()))); + const int sector = ((LinearWorld*)World::getWorld()) + ->getTrackSector(getWorldKartId()).getCurrentGraphNode(); + dist_to_sector = getXYZ().distance + (QuadGraph::get()->getNode(sector)->getCenter()); + + const Vec3& quad_normal = QuadGraph::get()->getNode(sector) + ->getNormal(); + const btQuaternion& q = getTrans().getRotation(); + const float roll = quad_normal.angle + ((Vec3(0, 1, 0).rotate(q.getAxis(), q.getAngle()))); if (World::getWorld()->getTrack()->isAutoRescueEnabled() && (!m_terrain_info->getMaterial() || !m_terrain_info->getMaterial()->hasGravity()) && !getKartAnimation() && fabs(roll) > 60 * DEGREE_TO_RAD && - fabs(getSpeed()) < 3.0f && - !m_flying) + fabs(getSpeed()) < 3.0f) { new RescueAnimation(this, /*is_auto_rescue*/true); } @@ -1322,8 +1328,6 @@ void Kart::update(float dt) Vec3 front(0, 0, getKartLength()*0.5f); m_xyz_front = getTrans()(front); - //m_terrain_info->update(getXYZ() + epsilon*(quadNormal), -quadNormal); - // After the physics step was done, the position of the wheels (as stored // in wheelInfo) is actually outdated, since the chassis was moved // according to the force acting from the wheels. So the cnter of the @@ -1348,7 +1352,7 @@ void Kart::update(float dt) // partly tunnels through the track). While tunneling should not be // happening (since Z velocity is clamped), the epsilon is left in place // just to be on the safe side (it will not hit the chassis itself). - from = from/4 + Vec3(0,0.3f,0); + from = from/4 + (getTrans().getBasis() * Vec3(0,0.3f,0)); m_terrain_info->update(getTrans().getBasis(), from); @@ -1373,13 +1377,6 @@ void Kart::update(float dt) const Vec3 *min, *max; World::getWorld()->getTrack()->getAABB(&min, &max); - // We do this for now because dist_to_sector is not defined - float dist_to_sector; - if (QuadGraph::get()) - dist_to_sector = getXYZ().distance(QuadGraph::get()->getNode(sector)->getCenter()); - else - dist_to_sector = 0; - if((min->getY() - getXYZ().getY() > 17 || dist_to_sector > 25) && !m_flying && !getKartAnimation()) new RescueAnimation(this); From ceb92f9b4a28e24561e9eff7d9396a9e8b5f4244 Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 10 Sep 2016 01:20:11 +0800 Subject: [PATCH 165/350] Try to completely fix rescue issue --- src/karts/abstract_kart.hpp | 2 ++ src/karts/kart.cpp | 60 +++++++++++++++++++--------------- src/karts/kart.hpp | 2 ++ src/karts/rescue_animation.cpp | 3 ++ src/modes/linear_world.cpp | 11 +------ src/modes/world.cpp | 5 ++- src/tracks/track.cpp | 17 +++------- 7 files changed, 47 insertions(+), 53 deletions(-) diff --git a/src/karts/abstract_kart.hpp b/src/karts/abstract_kart.hpp index 7124182b3..e56727170 100644 --- a/src/karts/abstract_kart.hpp +++ b/src/karts/abstract_kart.hpp @@ -432,6 +432,8 @@ public: /** Returns the terrain info oject. */ virtual const TerrainInfo *getTerrainInfo() const = 0; // ------------------------------------------------------------------------ + virtual void updateTerrainInfo() = 0; + // ------------------------------------------------------------------------ /** Called when the kart crashes against another kart. * \param k The kart that was hit. * \param update_attachments If true the attachment of this kart and the diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 75643c0e2..9319b1898 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1328,33 +1328,7 @@ void Kart::update(float dt) Vec3 front(0, 0, getKartLength()*0.5f); m_xyz_front = getTrans()(front); - // After the physics step was done, the position of the wheels (as stored - // in wheelInfo) is actually outdated, since the chassis was moved - // according to the force acting from the wheels. So the cnter of the - // chassis is not at the center of the wheels anymore, it is somewhat - // moved forward (depending on speed and fps). In very extreme cases - // (see bug 2246) the center of the chassis can actually be ahead of the - // front wheels. So if we do a raycast to detect the terrain from the - // current chassis, that raycast might be ahead of the wheels - which - // results in incorrect rescues (the wheels are still on the ground, - // but the raycast happens ahead of the front wheels and are over - // a rescue texture). - // To avoid this problem, we do the raycast for terrain detection from - // the center of the 4 wheel positions (in world coordinates). - - Vec3 from(0, 0, 0); - for (unsigned int i = 0; i < 4; i++) - from += m_vehicle->getWheelInfo(i).m_raycastInfo.m_hardPointWS; - - // Add a certain epsilon (0.3) to the height of the kart. This avoids - // problems of the ray being cast from under the track (which happened - // e.g. on tux tollway when jumping down from the ramp, when the chassis - // partly tunnels through the track). While tunneling should not be - // happening (since Z velocity is clamped), the epsilon is left in place - // just to be on the safe side (it will not hit the chassis itself). - from = from/4 + (getTrans().getBasis() * Vec3(0,0.3f,0)); - - m_terrain_info->update(getTrans().getBasis(), from); + updateTerrainInfo(); if(m_body->getBroadphaseHandle()) { @@ -1501,6 +1475,38 @@ void Kart::update(float dt) } // update +//----------------------------------------------------------------------------- +void Kart::updateTerrainInfo() +{ + // After the physics step was done, the position of the wheels (as stored + // in wheelInfo) is actually outdated, since the chassis was moved + // according to the force acting from the wheels. So the cnter of the + // chassis is not at the center of the wheels anymore, it is somewhat + // moved forward (depending on speed and fps). In very extreme cases + // (see bug 2246) the center of the chassis can actually be ahead of the + // front wheels. So if we do a raycast to detect the terrain from the + // current chassis, that raycast might be ahead of the wheels - which + // results in incorrect rescues (the wheels are still on the ground, + // but the raycast happens ahead of the front wheels and are over + // a rescue texture). + // To avoid this problem, we do the raycast for terrain detection from + // the center of the 4 wheel positions (in world coordinates). + + Vec3 from(0, 0, 0); + for (unsigned int i = 0; i < 4; i++) + from += m_vehicle->getWheelInfo(i).m_raycastInfo.m_hardPointWS; + + // Add a certain epsilon (0.3) to the height of the kart. This avoids + // problems of the ray being cast from under the track (which happened + // e.g. on tux tollway when jumping down from the ramp, when the chassis + // partly tunnels through the track). While tunneling should not be + // happening (since Z velocity is clamped), the epsilon is left in place + // just to be on the safe side (it will not hit the chassis itself). + from = from/4 + (getTrans().getBasis() * Vec3(0,0.3f,0)); + + m_terrain_info->update(getTrans().getBasis(), from); +} // updateTerrainInfo + //----------------------------------------------------------------------------- /** Show fire to go with a zipper. */ diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index 01c6607a5..838dc7305 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -436,6 +436,8 @@ public: /** Returns the terrain info oject. */ virtual const TerrainInfo *getTerrainInfo() const { return m_terrain_info; } // ------------------------------------------------------------------------ + virtual void updateTerrainInfo(); + // ------------------------------------------------------------------------ virtual void setOnScreenText(const wchar_t *text); // ------------------------------------------------------------------------ /** Returns the normal of the terrain the kart is over atm. This is diff --git a/src/karts/rescue_animation.cpp b/src/karts/rescue_animation.cpp index dbec7ff01..27990ea79 100644 --- a/src/karts/rescue_animation.cpp +++ b/src/karts/rescue_animation.cpp @@ -98,6 +98,9 @@ RescueAnimation::~RescueAnimation() camera->setMode(Camera::CM_NORMAL); } } + // Update the terrain info based on the moved position now, as + // updatePhysics in kart requires the normal from moved position to work + m_kart->updateTerrainInfo(); } // ~RescueAnimation // ---------------------------------------------------------------------------- diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index ccefd0eb9..678bf3bc2 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -661,18 +661,9 @@ btTransform LinearWorld::getRescueTransform(unsigned int index) const } else q1 = btQuaternion(Vec3(0,1,0),0); - btQuaternion q2(btVector3(0,1,0), m_track->getAngle(index)); - // First apply the heading change, than the 'parallelisation' to the plane + btQuaternion q2(btVector3(0,1,0), m_track->getAngle(index)); pos.setRotation(q1*q2); -#ifdef DEBUG - btQuaternion r = q1*q2; - btVector3 axis = r.getAxis(); - float angle = r.getAngle(); - //Log::debug("rescue", "%d angle %f axis %f %f %f pos %f %f %f", - // index, angle, axis.getX(),axis.getY(),axis.getZ(), - // xyz.getX(),xyz.getY(),xyz.getZ()); -#endif return pos; } // getRescueTransform diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 9253f9aef..08d619659 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -792,10 +792,9 @@ void World::moveKartTo(AbstractKart* kart, const btTransform &transform) { btTransform pos(transform); - btQuaternion rot = pos.getRotation(); - // Move the kart - Vec3 xyz = pos.getOrigin() + 0.5f*kart->getKartHeight() * kart->getNormal(); + Vec3 xyz = pos.getOrigin() + + pos.getBasis() * Vec3(0, 0.5f*kart->getKartHeight(), 0); pos.setOrigin(xyz); kart->setXYZ(xyz); kart->setRotation(pos.getRotation()); diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index 2152819c8..e7cd24d2e 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -2380,23 +2380,14 @@ const core::vector3df& Track::getSunRotation() bool Track::findGround(AbstractKart *kart) { - btVector3 to(kart->getXYZ()); - Vec3 quadNormal(0, 1, 0);; - if (QuadGraph::get()) - { - int sector = ((LinearWorld*)World::getWorld())->getTrackSector(kart->getWorldKartId()).getCurrentGraphNode(); - if (sector != QuadGraph::UNKNOWN_SECTOR) - quadNormal = QuadGraph::get()->getNode(sector)->getNormal(); - } - - to = to + -1000.0f*quadNormal; + const Vec3 &xyz = kart->getXYZ(); + Vec3 down = kart->getTrans().getBasis() * Vec3(0, -10000.0f, 0); // Material and hit point are not needed; const Material *m; Vec3 hit_point, normal; - bool over_ground = m_track_mesh->castRay(kart->getXYZ(), to, &hit_point, + bool over_ground = m_track_mesh->castRay(xyz, down, &hit_point, &m, &normal); - const Vec3 &xyz = kart->getXYZ(); if(!over_ground) { Log::warn("physics", "Kart at (%f %f %f) can not be dropped.", @@ -2432,7 +2423,7 @@ bool Track::findGround(AbstractKart *kart) // it). On the other hand this initial bouncing looks nice imho // - so I'll leave it in for now. float offset = kart->getKartProperties()->getSuspensionRest(); - t.setOrigin(hit_point + quadNormal * offset); + t.setOrigin(hit_point + normal * offset); kart->getBody()->setCenterOfMassTransform(t); kart->setTrans(t); From 278d9856326741270d24c55925cc91b4f8bdb56f Mon Sep 17 00:00:00 2001 From: Deve Date: Fri, 9 Sep 2016 22:23:01 +0200 Subject: [PATCH 166/350] Disable rtt that is not used in GLES renderer anyway --- src/graphics/irr_driver.hpp | 4 ++++ src/graphics/rtts.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/graphics/irr_driver.hpp b/src/graphics/irr_driver.hpp index 4927a993b..08bac725b 100644 --- a/src/graphics/irr_driver.hpp +++ b/src/graphics/irr_driver.hpp @@ -102,7 +102,9 @@ enum TypeFBO FBO_EIGHTH2, FBO_DISPLACE, FBO_BLOOM_1024, +#if !defined(USE_GLES2) FBO_SCALAR_1024, +#endif FBO_BLOOM_512, FBO_TMP_512, FBO_LENS_512, @@ -164,7 +166,9 @@ enum TypeRTT RTT_MLAA_TMP, RTT_BLOOM_1024, +#if !defined(USE_GLES2) RTT_SCALAR_1024, +#endif RTT_BLOOM_512, RTT_TMP_512, RTT_LENS_512, diff --git a/src/graphics/rtts.cpp b/src/graphics/rtts.cpp index 8fbe8b11d..de9b272e1 100644 --- a/src/graphics/rtts.cpp +++ b/src/graphics/rtts.cpp @@ -105,7 +105,9 @@ RTT::RTT(size_t width, size_t height) RenderTargetTextures[RTT_HALF2_R] = generateRTT(half, GL_R16F, GL_RED, GL_FLOAT); RenderTargetTextures[RTT_BLOOM_1024] = generateRTT(shadowsize0, GL_RGBA16F, GL_BGR, GL_FLOAT); +#if !defined(USE_GLES2) RenderTargetTextures[RTT_SCALAR_1024] = generateRTT(shadowsize0, GL_R32F, GL_RED, GL_FLOAT); +#endif RenderTargetTextures[RTT_BLOOM_512] = generateRTT(shadowsize1, GL_RGBA16F, GL_BGR, GL_FLOAT); RenderTargetTextures[RTT_TMP_512] = generateRTT(shadowsize1, GL_RGBA16F, GL_BGR, GL_FLOAT); RenderTargetTextures[RTT_LENS_512] = generateRTT(shadowsize1, GL_RGBA16F, GL_BGR, GL_FLOAT); @@ -191,9 +193,11 @@ RTT::RTT(size_t width, size_t height) somevector.push_back(RenderTargetTextures[RTT_BLOOM_1024]); FrameBuffers.push_back(new FrameBuffer(somevector, shadowsize0.Width, shadowsize0.Height)); somevector.clear(); +#if !defined(USE_GLES2) somevector.push_back(RenderTargetTextures[RTT_SCALAR_1024]); FrameBuffers.push_back(new FrameBuffer(somevector, shadowsize0.Width, shadowsize0.Height)); somevector.clear(); +#endif somevector.push_back(RenderTargetTextures[RTT_BLOOM_512]); FrameBuffers.push_back(new FrameBuffer(somevector, shadowsize1.Width, shadowsize1.Height)); From 37f3a8e19e04c07b203d7ebe2d51281bc9e54eb4 Mon Sep 17 00:00:00 2001 From: Deve Date: Fri, 9 Sep 2016 23:56:22 +0200 Subject: [PATCH 167/350] Fixed RTTs formats in GLES renderer. The problem is that textures with float internal formats can be used in framebuffers only when some extensions are available. This is a basic functionality and we need something that is supported on all drivers. As far as I tested, the basic integer GL_RGBA8 format works fine. Another problem is with GL_BGRA format. The extension with GL_BGRA support is available for almost every device and irrlicht is able to load all textures from files (it has a fallback mode anyway), but when it's used as a texture for framebuffer, it produces a black screen. Again it works fine with GL_RGBA format. The only issue that is not fixed is SSAO rendering. It uses linear depth rtt, which has GL_R32F type and which can't be easily changed to something other. I disabled SSAO for now, because it is too slow for embedded devices anyway. I didn't change anything in original OpenGL 3.x rendering, so everything should work as before. --- src/graphics/render.cpp | 2 + src/graphics/rtts.cpp | 94 +++++++++++++++++++++++++---------------- 2 files changed, 59 insertions(+), 37 deletions(-) diff --git a/src/graphics/render.cpp b/src/graphics/render.cpp index 3ef16eefc..68e2c8cf2 100644 --- a/src/graphics/render.cpp +++ b/src/graphics/render.cpp @@ -431,8 +431,10 @@ void IrrDriver::renderScene(scene::ICameraSceneNode * const camnode, unsigned po { PROFILER_PUSH_CPU_MARKER("- SSAO", 0xFF, 0xFF, 0x00); ScopedGPUTimer Timer(getGPUTimer(Q_SSAO)); + #if !defined(USE_GLES2) if (UserConfigParams::m_ssao) renderSSAO(); + #endif PROFILER_POP_CPU_MARKER(); } diff --git a/src/graphics/rtts.cpp b/src/graphics/rtts.cpp index de9b272e1..f0c9bbd94 100644 --- a/src/graphics/rtts.cpp +++ b/src/graphics/rtts.cpp @@ -78,47 +78,67 @@ RTT::RTT(size_t width, size_t height) // All RTTs are currently RGBA16F mostly with stencil. The four tmp RTTs are the same size // as the screen, for use in post-processing. - - RenderTargetTextures[RTT_TMP1] = generateRTT(res, GL_RGBA16F, GL_BGRA, GL_FLOAT); - RenderTargetTextures[RTT_TMP2] = generateRTT(res, GL_RGBA16F, GL_BGRA, GL_FLOAT); - RenderTargetTextures[RTT_TMP3] = generateRTT(res, GL_RGBA16F, GL_BGRA, GL_FLOAT); - RenderTargetTextures[RTT_TMP4] = generateRTT(res, GL_R16F, GL_RED, GL_FLOAT); - RenderTargetTextures[RTT_LINEAR_DEPTH] = generateRTT(res, GL_R32F, GL_RED, GL_FLOAT, linear_depth_mip_levels); - RenderTargetTextures[RTT_NORMAL_AND_DEPTH] = generateRTT(res, GL_RGBA16F, GL_RGBA, GL_FLOAT); - RenderTargetTextures[RTT_COLOR] = generateRTT(res, GL_RGBA16F, GL_BGRA, GL_FLOAT); - RenderTargetTextures[RTT_MLAA_COLORS] = generateRTT(res, GL_SRGB8_ALPHA8, GL_BGR, GL_UNSIGNED_BYTE); - RenderTargetTextures[RTT_MLAA_TMP] = generateRTT(res, GL_SRGB8_ALPHA8, GL_BGR, GL_UNSIGNED_BYTE); - RenderTargetTextures[RTT_MLAA_BLEND] = generateRTT(res, GL_SRGB8_ALPHA8, GL_BGR, GL_UNSIGNED_BYTE); - RenderTargetTextures[RTT_SSAO] = generateRTT(res, GL_R16F, GL_RED, GL_FLOAT); - RenderTargetTextures[RTT_DISPLACE] = generateRTT(res, GL_RGBA16F, GL_BGRA, GL_FLOAT); - RenderTargetTextures[RTT_DIFFUSE] = generateRTT(res, GL_R11F_G11F_B10F, GL_BGR, GL_FLOAT); - RenderTargetTextures[RTT_SPECULAR] = generateRTT(res, GL_R11F_G11F_B10F, GL_BGR, GL_FLOAT); - - RenderTargetTextures[RTT_HALF1] = generateRTT(half, GL_RGBA16F, GL_BGRA, GL_FLOAT); - RenderTargetTextures[RTT_QUARTER1] = generateRTT(quarter, GL_RGBA16F, GL_BGRA, GL_FLOAT); - RenderTargetTextures[RTT_EIGHTH1] = generateRTT(eighth, GL_RGBA16F, GL_BGRA, GL_FLOAT); - RenderTargetTextures[RTT_HALF1_R] = generateRTT(half, GL_R16F, GL_RED, GL_FLOAT); - - RenderTargetTextures[RTT_HALF2] = generateRTT(half, GL_RGBA16F, GL_BGRA, GL_FLOAT); - RenderTargetTextures[RTT_QUARTER2] = generateRTT(quarter, GL_RGBA16F, GL_BGRA, GL_FLOAT); - RenderTargetTextures[RTT_EIGHTH2] = generateRTT(eighth, GL_RGBA16F, GL_BGRA, GL_FLOAT); - RenderTargetTextures[RTT_HALF2_R] = generateRTT(half, GL_R16F, GL_RED, GL_FLOAT); - - RenderTargetTextures[RTT_BLOOM_1024] = generateRTT(shadowsize0, GL_RGBA16F, GL_BGR, GL_FLOAT); + #if !defined(USE_GLES2) - RenderTargetTextures[RTT_SCALAR_1024] = generateRTT(shadowsize0, GL_R32F, GL_RED, GL_FLOAT); + GLint rgba_internal_format = GL_RGBA16F; + GLint rgba_format = GL_BGRA; + GLint red_internal_format = GL_R16F; + GLint red32_internal_format = GL_R32F; + GLint red_format = GL_RED; + GLint rgb_format = GL_BGR; + GLint diffuse_specular_internal_format = GL_R11F_G11F_B10F; + GLint type = GL_FLOAT; +#else + GLint rgba_internal_format = GL_RGBA8; + GLint rgba_format = GL_RGBA; + GLint red_internal_format = GL_R8; + GLint red32_internal_format = GL_R8; + GLint red_format = GL_RED; + GLint rgb_format = GL_RGB; + GLint diffuse_specular_internal_format = GL_RGBA8; + GLint type = GL_UNSIGNED_BYTE; #endif - RenderTargetTextures[RTT_BLOOM_512] = generateRTT(shadowsize1, GL_RGBA16F, GL_BGR, GL_FLOAT); - RenderTargetTextures[RTT_TMP_512] = generateRTT(shadowsize1, GL_RGBA16F, GL_BGR, GL_FLOAT); - RenderTargetTextures[RTT_LENS_512] = generateRTT(shadowsize1, GL_RGBA16F, GL_BGR, GL_FLOAT); + + RenderTargetTextures[RTT_TMP1] = generateRTT(res, rgba_internal_format, rgba_format, type); + RenderTargetTextures[RTT_TMP2] = generateRTT(res, rgba_internal_format, rgba_format, type); + RenderTargetTextures[RTT_TMP3] = generateRTT(res, rgba_internal_format, rgba_format, type); + RenderTargetTextures[RTT_TMP4] = generateRTT(res, red_internal_format, red_format, type); + RenderTargetTextures[RTT_LINEAR_DEPTH] = generateRTT(res, red32_internal_format, red_format, type, linear_depth_mip_levels); + RenderTargetTextures[RTT_NORMAL_AND_DEPTH] = generateRTT(res, rgba_internal_format, GL_RGBA, type); + RenderTargetTextures[RTT_COLOR] = generateRTT(res, rgba_internal_format, rgba_format, type); + RenderTargetTextures[RTT_MLAA_COLORS] = generateRTT(res, GL_SRGB8_ALPHA8, rgb_format, GL_UNSIGNED_BYTE); + RenderTargetTextures[RTT_MLAA_TMP] = generateRTT(res, GL_SRGB8_ALPHA8, rgb_format, GL_UNSIGNED_BYTE); + RenderTargetTextures[RTT_MLAA_BLEND] = generateRTT(res, GL_SRGB8_ALPHA8, rgb_format, GL_UNSIGNED_BYTE); + RenderTargetTextures[RTT_SSAO] = generateRTT(res, red_internal_format, red_format, type); + RenderTargetTextures[RTT_DISPLACE] = generateRTT(res, rgba_internal_format, rgba_format, type); + RenderTargetTextures[RTT_DIFFUSE] = generateRTT(res, diffuse_specular_internal_format, rgb_format, type); + RenderTargetTextures[RTT_SPECULAR] = generateRTT(res, diffuse_specular_internal_format, rgb_format, type); + + RenderTargetTextures[RTT_HALF1] = generateRTT(half, rgba_internal_format, rgba_format, type); + RenderTargetTextures[RTT_QUARTER1] = generateRTT(quarter, rgba_internal_format, rgba_format, type); + RenderTargetTextures[RTT_EIGHTH1] = generateRTT(eighth, rgba_internal_format, rgba_format, type); + RenderTargetTextures[RTT_HALF1_R] = generateRTT(half, red_internal_format, red_format, type); + + RenderTargetTextures[RTT_HALF2] = generateRTT(half, rgba_internal_format, rgba_format, type); + RenderTargetTextures[RTT_QUARTER2] = generateRTT(quarter, rgba_internal_format, rgba_format, type); + RenderTargetTextures[RTT_EIGHTH2] = generateRTT(eighth, rgba_internal_format, rgba_format, type); + RenderTargetTextures[RTT_HALF2_R] = generateRTT(half, red_internal_format, red_format, type); + + RenderTargetTextures[RTT_BLOOM_1024] = generateRTT(shadowsize0, rgba_internal_format, rgb_format, type); +#if !defined(USE_GLES2) + RenderTargetTextures[RTT_SCALAR_1024] = generateRTT(shadowsize0, red32_internal_format, red_format, type); +#endif + RenderTargetTextures[RTT_BLOOM_512] = generateRTT(shadowsize1, rgba_internal_format, rgb_format, type); + RenderTargetTextures[RTT_TMP_512] = generateRTT(shadowsize1, rgba_internal_format, rgb_format, type); + RenderTargetTextures[RTT_LENS_512] = generateRTT(shadowsize1, rgba_internal_format, rgb_format, type); - RenderTargetTextures[RTT_BLOOM_256] = generateRTT(shadowsize2, GL_RGBA16F, GL_BGR, GL_FLOAT); - RenderTargetTextures[RTT_TMP_256] = generateRTT(shadowsize2, GL_RGBA16F, GL_BGR, GL_FLOAT); - RenderTargetTextures[RTT_LENS_256] = generateRTT(shadowsize2, GL_RGBA16F, GL_BGR, GL_FLOAT); + RenderTargetTextures[RTT_BLOOM_256] = generateRTT(shadowsize2, rgba_internal_format, rgb_format, type); + RenderTargetTextures[RTT_TMP_256] = generateRTT(shadowsize2, rgba_internal_format, rgb_format, type); + RenderTargetTextures[RTT_LENS_256] = generateRTT(shadowsize2, rgba_internal_format, rgb_format, type); - RenderTargetTextures[RTT_BLOOM_128] = generateRTT(shadowsize3, GL_RGBA16F, GL_BGR, GL_FLOAT); - RenderTargetTextures[RTT_TMP_128] = generateRTT(shadowsize3, GL_RGBA16F, GL_BGR, GL_FLOAT); - RenderTargetTextures[RTT_LENS_128] = generateRTT(shadowsize3, GL_RGBA16F, GL_BGR, GL_FLOAT); + RenderTargetTextures[RTT_BLOOM_128] = generateRTT(shadowsize3, rgba_internal_format, rgb_format, type); + RenderTargetTextures[RTT_TMP_128] = generateRTT(shadowsize3, rgba_internal_format, rgb_format, type); + RenderTargetTextures[RTT_LENS_128] = generateRTT(shadowsize3, rgba_internal_format, rgb_format, type); std::vector somevector; somevector.push_back(RenderTargetTextures[RTT_SSAO]); From fb1c049906dc4f1d61430b5851fce492bd48cb74 Mon Sep 17 00:00:00 2001 From: Deve Date: Sat, 10 Sep 2016 00:17:47 +0200 Subject: [PATCH 168/350] Fixed skybox in GLES renderer --- src/graphics/skybox.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/graphics/skybox.cpp b/src/graphics/skybox.cpp index cf51bc70c..07d90f719 100644 --- a/src/graphics/skybox.cpp +++ b/src/graphics/skybox.cpp @@ -170,6 +170,15 @@ void Skybox::generateCubeMapFromTextures() image->copyToScaling(rgba[i], size, size); image->drop(); +#if defined(USE_GLES2) + for (unsigned int j = 0; j < size * size; j++) + { + char tmp_val = rgba[i][j*4]; + rgba[i][j*4] = rgba[i][j*4 + 2]; + rgba[i][j*4 + 2] = tmp_val; + } +#endif + if (i == 2 || i == 3) { char *tmp = new char[size * size * 4]; @@ -188,15 +197,14 @@ void Skybox::generateCubeMapFromTextures() #if !defined(USE_GLES2) GLint internal_format = CVS->isTextureCompressionEnabled() ? GL_COMPRESSED_SRGB_ALPHA : GL_SRGB_ALPHA; + GLint format = GL_BGRA; #else - // The GL_SRGB_ALPHA_EXT and GL_SRGB8_ALPHA8 formats are available in - // OpenGL ES, but they produce black texture for some reason. - // The basic GL_RGBA format works fine though. - GLint internal_format = GL_RGBA; + GLint internal_format = GL_RGBA8; + GLint format = GL_RGBA; #endif glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, - internal_format, size, size, 0, GL_BGRA, + internal_format, size, size, 0, format, GL_UNSIGNED_BYTE, (GLvoid*)rgba[i]); } glGenerateMipmap(GL_TEXTURE_CUBE_MAP); @@ -213,8 +221,13 @@ void Skybox::generateSpecularCubemap() size_t cubemap_size = 256; for (int i = 0; i < 6; i++) { +#if !defined(USE_GLES2) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA16F, cubemap_size, cubemap_size, 0, GL_BGRA, GL_FLOAT, 0); +#else + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA8, + cubemap_size, cubemap_size, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); +#endif } glGenerateMipmap(GL_TEXTURE_CUBE_MAP); From fde9ccd53e0b1a37eb115803958de28745648221 Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 10 Sep 2016 11:09:52 +0800 Subject: [PATCH 169/350] Move updateTerrainInfo to a better location --- src/karts/rescue_animation.cpp | 3 --- src/modes/world.cpp | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/karts/rescue_animation.cpp b/src/karts/rescue_animation.cpp index 27990ea79..dbec7ff01 100644 --- a/src/karts/rescue_animation.cpp +++ b/src/karts/rescue_animation.cpp @@ -98,9 +98,6 @@ RescueAnimation::~RescueAnimation() camera->setMode(Camera::CM_NORMAL); } } - // Update the terrain info based on the moved position now, as - // updatePhysics in kart requires the normal from moved position to work - m_kart->updateTerrainInfo(); } // ~RescueAnimation // ---------------------------------------------------------------------------- diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 08d619659..e69aae400 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -811,6 +811,10 @@ void World::moveKartTo(AbstractKart* kart, const btTransform &transform) // This will set the physics transform m_track->findGround(kart); + // Update the terrain info based on the moved position now, as + // updatePhysics in kart requires the normal from moved position to work + kart->updateTerrainInfo(); + } // moveKartTo // ---------------------------------------------------------------------------- From 7cfd9752d0d45816ce2b49f02b6e09db9e6d0520 Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 10 Sep 2016 11:11:52 +0800 Subject: [PATCH 170/350] Fix restarting the race when karts are up-side-down --- src/karts/kart.cpp | 11 ++++---- src/modes/world.cpp | 64 ++------------------------------------------- 2 files changed, 8 insertions(+), 67 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 9319b1898..c601a5af3 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -295,7 +295,10 @@ void Kart::reset() // mode) - but only if they actually have a body (e.g. ghost karts // don't have one). if(m_body) + { + World::getWorld()->getPhysics()->removeKart(this); World::getWorld()->getPhysics()->addKart(this); + } m_min_nitro_time = 0.0f; @@ -684,10 +687,8 @@ void Kart::createPhysics() wheel.m_frictionSlip = m_kart_properties->getFrictionSlip(); wheel.m_rollInfluence = m_kart_properties->getStabilityRollInfluence(); } - // Obviously these allocs have to be properly managed/freed - btTransform t; - t.setIdentity(); - World::getWorld()->getPhysics()->addKart(this); + // Body to be added in reset() which allows complete reset kart when + // restarting the race } // createPhysics @@ -1345,7 +1346,7 @@ void Kart::update(float dt) float g = World::getWorld()->getTrack()->getGravity(); Vec3 gravity(0, -g, 0); btRigidBody *body = getVehicle()->getRigidBody(); - //body->setGravity(gravity); + body->setGravity(gravity); } // let kart fall a bit before rescuing const Vec3 *min, *max; diff --git a/src/modes/world.cpp b/src/modes/world.cpp index e69aae400..334afaba9 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -693,70 +693,10 @@ void World::resetAllKarts() } } - bool all_finished=false; - // kart->isInRest() is not fully correct, since it only takes the - // velocity in count, which might be close to zero when the kart - // is just hitting the floor, before being pushed up again by - // the suspension. So we just do a longer initial simulation, - // which should be long enough for all karts to be firmly on ground. + // Do a longer initial simulation, which should be long enough for all + // karts to be firmly on ground. for(int i=0; i<60; i++) m_physics->update(1.f/60.f); - // Stil wait will all karts are in rest (and handle the case that a kart - // fell through the ground, which can happen if a kart falls for a long - // time, therefore having a high speed when hitting the ground. - int count = 0; - while(!all_finished) - { - if (count++ > 100) - { - Log::error("World", "Infinite loop waiting for all_finished?"); - break; - } - m_physics->update(1.f/60.f); - all_finished=true; - for ( KartList::iterator i=m_karts.begin(); i!=m_karts.end(); i++) - { - if ((*i)->isGhostKart()) continue; - if(!(*i)->isInRest()) - { - Vec3 normal; - Vec3 hit_point; - const Material *material; - // We can't use (*i)->getXYZ(), since this is only defined - // after update() was called. Instead we have to get the - // real position of the rigid body. - btTransform t; - (*i)->getBody()->getMotionState()->getWorldTransform(t); - // This test can not be done only once before the loop, since - // it can happen that the kart falls through the track later! - Vec3 to = t.getOrigin()+Vec3(0, -10000, 0); - m_track->getTriangleMesh().castRay(t.getOrigin(), to, - &hit_point, &material, - &normal); - if(!material) - { - Log::error("World", - "No valid starting position for kart %d " - "on track %s.", - (int)(i-m_karts.begin()), - m_track->getIdent().c_str()); - if (UserConfigParams::m_artist_debug_mode) - { - Log::warn("World", "Activating fly mode."); - (*i)->flyUp(); - continue; - } - else - { - exit(-1); - } - } - all_finished=false; - break; - } - } - } // while - for ( KartList::iterator i=m_karts.begin(); i!=m_karts.end(); i++) { (*i)->kartIsInRestNow(); From b3d5e58dd1cfc82c92b7d8f34c803e4d6624d77a Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 10 Sep 2016 14:32:55 +0800 Subject: [PATCH 171/350] Revert enable sliding for upside down tracks. Actually !getMaterial()->highTireAdhesion() is always false for upside down texture, so the below code was useless to test with normal --- src/karts/kart.cpp | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index c601a5af3..7f1433c38 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -2423,21 +2423,10 @@ void Kart::updateSliding() (!getMaterial() || !getMaterial()->highTireAdhesion())) { const btMatrix3x3 &m = m_vehicle->getChassisWorldTransform().getBasis(); - float distanceFromUp; - if (QuadGraph::get()) - { - int sector = ((LinearWorld*)World::getWorld())->getTrackSector(getWorldKartId()).getCurrentGraphNode(); - Vec3 quadNormal = QuadGraph::get()->getNode(sector)->getNormal(); - Vec3 kart_up = m.getColumn(1); - distanceFromUp = kart_up.dot(quadNormal); - } - else - { - // To get the angle between up=(0,1,0), we have to do: - // m*(0,1,0) to get the up vector of the kart, then the - // scalar product between this and (0,1,0) - which is m[1][1]: - distanceFromUp = m[1][1]; - } + // To get the angle between up=(0,1,0), we have to do: + // m*(0,1,0) to get the up vector of the kart, then the + // scalar product between this and (0,1,0) - which is m[1][1]: + float distanceFromUp = m[1][1]; if (distanceFromUp < 0.85f) { From c37f75bf900868bb6a5317a8716fd1d04365e886 Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 10 Sep 2016 16:09:21 +0800 Subject: [PATCH 172/350] Improve item projection on upside down surface --- src/items/bowling.cpp | 14 ++------------ src/items/cake.cpp | 20 ++++++-------------- src/items/flyable.cpp | 24 +++++++++++++----------- src/items/flyable.hpp | 2 +- src/items/plunger.cpp | 1 - 5 files changed, 22 insertions(+), 39 deletions(-) diff --git a/src/items/bowling.cpp b/src/items/bowling.cpp index 41b95b9e4..9ad5caf26 100644 --- a/src/items/bowling.cpp +++ b/src/items/bowling.cpp @@ -58,21 +58,11 @@ Bowling::Bowling(AbstractKart *kart) if(m_speed < min_speed) m_speed = min_speed; } - Vec3 quadNormal; - if (race_manager->getMinorMode() != RaceManager::MINOR_MODE_3_STRIKES && - race_manager->getMinorMode() != RaceManager::MINOR_MODE_SOCCER) - { - unsigned int sector = ((LinearWorld*)World::getWorld())-> - getTrackSector(kart->getWorldKartId()).getCurrentGraphNode(); - quadNormal = QuadGraph::get()->getNode(sector)->getNormal(); - } - else - quadNormal = btVector3(.0f, 1.0f, .0f); - + const Vec3& normal = kart->getNormal(); createPhysics(y_offset, btVector3(0.0f, 0.0f, m_speed*2), new btSphereShape(0.5f*m_extend.getY()), 0.8f /*restitution*/, - -70.0f*quadNormal /*gravity*/, + -70.0f*normal /*gravity*/, true /*rotates*/); // Even if the ball is fired backwards, m_speed must be positive, // otherwise the ball can start to vibrate when energy is added. diff --git a/src/items/cake.cpp b/src/items/cake.cpp index 2084331e1..cd2098dea 100644 --- a/src/items/cake.cpp +++ b/src/items/cake.cpp @@ -37,16 +37,10 @@ Cake::Cake (AbstractKart *kart) : Flyable(kart, PowerupManager::POWERUP_CAKE) setDoTerrainInfo(false); - Vec3 gravity_vector; - if (race_manager->getMinorMode() != RaceManager::MINOR_MODE_3_STRIKES && - race_manager->getMinorMode() != RaceManager::MINOR_MODE_SOCCER) - { - btQuaternion q = kart->getTrans().getRotation(); - gravity_vector = Vec3(0, -1, 0).rotate(q.getAxis(), q.getAngle()); - gravity_vector = gravity_vector.normalize() * m_gravity; - } - else - gravity_vector = Vec3(0.0f, -1.0f, 0.0f) * m_gravity; + btVector3 gravity_vector; + btQuaternion q = kart->getTrans().getRotation(); + gravity_vector = Vec3(0, -1, 0).rotate(q.getAxis(), q.getAngle()); + gravity_vector = gravity_vector.normalize() * m_gravity; // A bit of a hack: the mass of this kinematic object is still 1.0 // (see flyable), which enables collisions. I tried setting // collisionFilterGroup/mask, but still couldn't get this object to @@ -100,12 +94,11 @@ Cake::Cake (AbstractKart *kart) : Flyable(kart, PowerupManager::POWERUP_CAKE) btQuaternion q; q = trans.getRotation() * btQuaternion(btVector3(0, 1, 0), fire_angle); trans.setRotation(q); - m_initial_velocity = Vec3(0.0f, up_velocity, m_speed); createPhysics(forward_offset, m_initial_velocity, new btCylinderShape(0.5f*m_extend), - 0.5f /* restitution */, btVector3(gravity_vector), + 0.5f /* restitution */, gravity_vector, true /* rotation */, false /* backwards */, &trans); } else @@ -119,7 +112,7 @@ Cake::Cake (AbstractKart *kart) : Flyable(kart, PowerupManager::POWERUP_CAKE) createPhysics(forward_offset, m_initial_velocity, new btCylinderShape(0.5f*m_extend), - 0.5f /* restitution */, btVector3(gravity_vector), + 0.5f /* restitution */, gravity_vector, true /* rotation */, backwards, &trans); } @@ -142,7 +135,6 @@ void Cake::init(const XMLNode &node, scene::IMesh *cake_model) { Flyable::init(node, cake_model, PowerupManager::POWERUP_CAKE); float max_distance = 80.0f; - m_gravity = 9.8f; if (m_gravity < 0) m_gravity *= -1.0f; diff --git a/src/items/flyable.cpp b/src/items/flyable.cpp index 980a2065f..9c22be6dd 100644 --- a/src/items/flyable.cpp +++ b/src/items/flyable.cpp @@ -28,6 +28,7 @@ #include "graphics/explosion.hpp" #include "graphics/irr_driver.hpp" +#include "graphics/material.hpp" #include "graphics/mesh_tools.hpp" #include "graphics/stars.hpp" #include "io/xml_node.hpp" @@ -101,7 +102,7 @@ Flyable::Flyable(AbstractKart *kart, PowerupManager::PowerupType type, */ void Flyable::createPhysics(float forw_offset, const Vec3 &velocity, btCollisionShape *shape, - float restitution, const btVector3 gravity, + float restitution, const btVector3& gravity, const bool rotates, const bool turn_around, const btTransform* custom_direction) { @@ -341,8 +342,6 @@ void Flyable::getLinearKartItemIntersection (const Vec3 &origin, float aimX = time*target_x_speed + relative_target_kart_loc.getX(); float aimZ = time*target_z_speed + relative_target_kart_loc.getZ(); - Vec3 velocityXZ = Vec3(aimX, 0, aimZ).normalize() * item_XZ_speed; - assert(time!=0); float angle = atan2f(aimX, aimZ); @@ -399,19 +398,22 @@ bool Flyable::updateAndDelete(float dt) // Add the position offset so that the flyable can adjust its position // (usually to do the raycast from a slightly higher position to avoid // problems finding the terrain in steep uphill sections). - // Towards is a unit vector. so we can multiply -towards to offset the position - // by one unit. + // Towards is a unit vector. so we can multiply -towards to offset the + // position by one unit. TerrainInfo::update(xyz + m_position_offset*(-towards), towards); - if (race_manager->getMinorMode() != RaceManager::MINOR_MODE_3_STRIKES && - race_manager->getMinorMode() != RaceManager::MINOR_MODE_SOCCER) + + // Make flyable anti-gravity when the it's projected on such surface + const Material* m = TerrainInfo::getMaterial(); + if (m && m->hasGravity()) { - Vec3 normal = TerrainInfo::getNormal(); - float g = World::getWorld()->getTrack()->getGravity(); - getBody()->setGravity(-g*normal); + getBody()->setGravity(TerrainInfo::getNormal() * -70.0f); + } + else + { + getBody()->setGravity(Vec3(0, 1, 0) * -70.0f); } } - if(m_adjust_up_velocity) { float hat = (xyz - getHitPoint()).length(); diff --git a/src/items/flyable.hpp b/src/items/flyable.hpp index 4e2f25b45..8daa0fdb5 100644 --- a/src/items/flyable.hpp +++ b/src/items/flyable.hpp @@ -149,7 +149,7 @@ protected: const Vec3 &velocity, btCollisionShape *shape, float restitution, - const btVector3 gravity = btVector3(0.0f, 0.0f,0.0f), + const btVector3& gravity=btVector3(0.0f,0.0f,0.0f), const bool rotates=false, const bool turn_around=false, const btTransform* customDirection=NULL); diff --git a/src/items/plunger.cpp b/src/items/plunger.cpp index 2d8077295..10a32685a 100644 --- a/src/items/plunger.cpp +++ b/src/items/plunger.cpp @@ -58,7 +58,6 @@ Plunger::Plunger(AbstractKart *kart) kart /* search in front of this kart */, m_reverse_mode); btTransform kart_transform = kart->getAlignedTransform(); - btMatrix3x3 kart_rotation = kart_transform.getBasis(); float heading =kart->getHeading(); float pitch = kart->getTerrainPitch(heading); From 2415340caebf0c3463f88f48d0e2fb752dd22b45 Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 11 Sep 2016 10:07:43 +0800 Subject: [PATCH 173/350] Try to fix too bouncy bowling ball --- src/items/bowling.cpp | 2 +- src/karts/kart.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/items/bowling.cpp b/src/items/bowling.cpp index 9ad5caf26..e24a2ceb5 100644 --- a/src/items/bowling.cpp +++ b/src/items/bowling.cpp @@ -61,7 +61,7 @@ Bowling::Bowling(AbstractKart *kart) const Vec3& normal = kart->getNormal(); createPhysics(y_offset, btVector3(0.0f, 0.0f, m_speed*2), new btSphereShape(0.5f*m_extend.getY()), - 0.8f /*restitution*/, + 0.4f /*restitution*/, -70.0f*normal /*gravity*/, true /*rotates*/); // Even if the ball is fired backwards, m_speed must be positive, diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 7f1433c38..5b81071ae 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1481,7 +1481,7 @@ void Kart::updateTerrainInfo() { // After the physics step was done, the position of the wheels (as stored // in wheelInfo) is actually outdated, since the chassis was moved - // according to the force acting from the wheels. So the cnter of the + // according to the force acting from the wheels. So the center of the // chassis is not at the center of the wheels anymore, it is somewhat // moved forward (depending on speed and fps). In very extreme cases // (see bug 2246) the center of the chassis can actually be ahead of the From ba047a4f992c6a6946c4dd2cfd7b0f372c5322b5 Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 11 Sep 2016 15:35:42 +0800 Subject: [PATCH 174/350] Keep flying karts parallel to the ground in the physics, not outside Like 44da1fb4f2ab1b9b1fe75724490ee7a3bade740f, but takes terrain into account --- src/karts/abstract_kart.hpp | 2 - src/karts/kart.cpp | 86 ++++++++++++------------------------- src/karts/kart.hpp | 2 - src/modes/world.cpp | 4 -- src/physics/btKart.cpp | 11 +++-- 5 files changed, 34 insertions(+), 71 deletions(-) diff --git a/src/karts/abstract_kart.hpp b/src/karts/abstract_kart.hpp index e56727170..7124182b3 100644 --- a/src/karts/abstract_kart.hpp +++ b/src/karts/abstract_kart.hpp @@ -432,8 +432,6 @@ public: /** Returns the terrain info oject. */ virtual const TerrainInfo *getTerrainInfo() const = 0; // ------------------------------------------------------------------------ - virtual void updateTerrainInfo() = 0; - // ------------------------------------------------------------------------ /** Called when the kart crashes against another kart. * \param k The kart that was hit. * \param update_attachments If true the attachment of this kart and the diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 5b81071ae..39c97d243 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1329,7 +1329,33 @@ void Kart::update(float dt) Vec3 front(0, 0, getKartLength()*0.5f); m_xyz_front = getTrans()(front); - updateTerrainInfo(); + // After the physics step was done, the position of the wheels (as stored + // in wheelInfo) is actually outdated, since the chassis was moved + // according to the force acting from the wheels. So the center of the + // chassis is not at the center of the wheels anymore, it is somewhat + // moved forward (depending on speed and fps). In very extreme cases + // (see bug 2246) the center of the chassis can actually be ahead of the + // front wheels. So if we do a raycast to detect the terrain from the + // current chassis, that raycast might be ahead of the wheels - which + // results in incorrect rescues (the wheels are still on the ground, + // but the raycast happens ahead of the front wheels and are over + // a rescue texture). + // To avoid this problem, we do the raycast for terrain detection from + // the center of the 4 wheel positions (in world coordinates). + + Vec3 from(0, 0, 0); + for (unsigned int i = 0; i < 4; i++) + from += m_vehicle->getWheelInfo(i).m_raycastInfo.m_hardPointWS; + + // Add a certain epsilon (0.3) to the height of the kart. This avoids + // problems of the ray being cast from under the track (which happened + // e.g. on tux tollway when jumping down from the ramp, when the chassis + // partly tunnels through the track). While tunneling should not be + // happening (since Z velocity is clamped), the epsilon is left in place + // just to be on the safe side (it will not hit the chassis itself). + from = from/4 + (getTrans().getBasis() * Vec3(0,0.3f,0)); + + m_terrain_info->update(getTrans().getBasis(), from); if(m_body->getBroadphaseHandle()) { @@ -1476,38 +1502,6 @@ void Kart::update(float dt) } // update -//----------------------------------------------------------------------------- -void Kart::updateTerrainInfo() -{ - // After the physics step was done, the position of the wheels (as stored - // in wheelInfo) is actually outdated, since the chassis was moved - // according to the force acting from the wheels. So the center of the - // chassis is not at the center of the wheels anymore, it is somewhat - // moved forward (depending on speed and fps). In very extreme cases - // (see bug 2246) the center of the chassis can actually be ahead of the - // front wheels. So if we do a raycast to detect the terrain from the - // current chassis, that raycast might be ahead of the wheels - which - // results in incorrect rescues (the wheels are still on the ground, - // but the raycast happens ahead of the front wheels and are over - // a rescue texture). - // To avoid this problem, we do the raycast for terrain detection from - // the center of the 4 wheel positions (in world coordinates). - - Vec3 from(0, 0, 0); - for (unsigned int i = 0; i < 4; i++) - from += m_vehicle->getWheelInfo(i).m_raycastInfo.m_hardPointWS; - - // Add a certain epsilon (0.3) to the height of the kart. This avoids - // problems of the ray being cast from under the track (which happened - // e.g. on tux tollway when jumping down from the ramp, when the chassis - // partly tunnels through the track). While tunneling should not be - // happening (since Z velocity is clamped), the epsilon is left in place - // just to be on the safe side (it will not hit the chassis itself). - from = from/4 + (getTrans().getBasis() * Vec3(0,0.3f,0)); - - m_terrain_info->update(getTrans().getBasis(), from); -} // updateTerrainInfo - //----------------------------------------------------------------------------- /** Show fire to go with a zipper. */ @@ -2199,32 +2193,6 @@ void Kart::updatePhysics(float dt) m_max_speed->setMinSpeed(min_speed); m_max_speed->update(dt); - // If the kart is flying, keep its up-axis aligned to gravity (which in - // turn typically means the kart is parallel to the ground). This avoids - // that the kart rotates in mid-air and lands on its side. - if(m_vehicle->getNumWheelsOnGround()==0) - { - Vec3 diff = getXYZ() - getTerrainInfo()->getHitPoint(); - float height = diff.dot(getNormal()); - if(height>0.5f && !m_flying) - { - btVector3 kart_up = getTrans().getBasis().getColumn(1); // up vector - btVector3 new_up = 0.9f * kart_up + 0.1f * getNormal(); - // Get the rotation (hpr) based on current heading. - Vec3 rotation(getHeading(), new_up); - btMatrix3x3 m; - m.setEulerZYX(rotation.getX(), rotation.getY(), rotation.getZ()); - // We can't use getXYZ() for the position here, since the position is - // based on interpolation, while the actual center-of-mass-transform - // is based on the actual value every 1/60 of a second (using getXYZ() - // would result in the kart being pushed ahead a bit, making it jump - // much further, depending on fps) - btTransform new_trans(m, m_body->getCenterOfMassTransform().getOrigin()); - //setTrans(new_trans); - m_body->setCenterOfMassTransform(new_trans); - } - } - // To avoid tunneling (which can happen on long falls), clamp the // velocity in Y direction. Tunneling can happen if the Y velocity // is larger than the maximum suspension travel (per frame), since then diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index 838dc7305..01c6607a5 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -436,8 +436,6 @@ public: /** Returns the terrain info oject. */ virtual const TerrainInfo *getTerrainInfo() const { return m_terrain_info; } // ------------------------------------------------------------------------ - virtual void updateTerrainInfo(); - // ------------------------------------------------------------------------ virtual void setOnScreenText(const wchar_t *text); // ------------------------------------------------------------------------ /** Returns the normal of the terrain the kart is over atm. This is diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 334afaba9..a69ff36de 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -751,10 +751,6 @@ void World::moveKartTo(AbstractKart* kart, const btTransform &transform) // This will set the physics transform m_track->findGround(kart); - // Update the terrain info based on the moved position now, as - // updatePhysics in kart requires the normal from moved position to work - kart->updateTerrainInfo(); - } // moveKartTo // ---------------------------------------------------------------------------- diff --git a/src/physics/btKart.cpp b/src/physics/btKart.cpp index 63037b618..b2c0d50f9 100644 --- a/src/physics/btKart.cpp +++ b/src/physics/btKart.cpp @@ -22,6 +22,7 @@ #include "LinearMath/btIDebugDraw.h" #include "BulletDynamics/ConstraintSolver/btContactConstraint.h" +#include "graphics/material.hpp" #include "karts/kart.hpp" #include "modes/world.hpp" #include "physics/triangle_mesh.hpp" @@ -445,7 +446,9 @@ void btKart::updateVehicle( btScalar step ) if(m_num_wheels_on_ground==0) { btVector3 kart_up = getChassisWorldTransform().getBasis().getColumn(1); - btVector3 terrain_up(0,1,0); + btVector3 terrain_up = + m_kart->getMaterial() && m_kart->getMaterial()->hasGravity() ? + m_kart->getNormal() : Vec3(0, 1, 0); // Length of axis depends on the angle - i.e. the further awat // the kart is from being upright, the larger the applied impulse // will be, resulting in fast changes when the kart is on its @@ -522,9 +525,9 @@ void btKart::updateVehicle( btScalar step ) for (int i=0;igetCenterOfMassPosition(); - btVector3 vel = getRigidBody()->getVelocityInLocalPoint(relpos); + //btVector3 relpos = wheel.m_raycastInfo.m_hardPointWS + // - getRigidBody()->getCenterOfMassPosition(); + //btVector3 vel = getRigidBody()->getVelocityInLocalPoint(relpos); if (wheel.m_raycastInfo.m_isInContact) { From f687454313418c76ba4acf0bcc36c7897f72a38a Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 11 Sep 2016 16:31:57 +0800 Subject: [PATCH 175/350] Fix item positioning on Node3D --- src/tracks/track.cpp | 78 ++++++++++++++++++-------------------------- src/tracks/track.hpp | 1 - 2 files changed, 31 insertions(+), 48 deletions(-) diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index e7cd24d2e..c18b4c4df 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -58,6 +58,7 @@ #include "tracks/check_manager.hpp" #include "tracks/graph_node.hpp" #include "tracks/model_definition_loader.hpp" +#include "tracks/node_3d.hpp" #include "tracks/quad_graph.hpp" #include "tracks/track_manager.hpp" #include "tracks/track_object_manager.hpp" @@ -2262,19 +2263,40 @@ void Track::itemCommand(const XMLNode *node) } Vec3 loc(xyz); - // if only 2d coordinates are given, let the item fall from very high - if(drop) + + // Test if the item lies on a 3d node, if so adjust the normal + // Also do a raycast if drop item is given + Vec3 normal(0, 1, 0); + Vec3 hit_point = loc; + Node3D* node_3d = NULL; + if (QuadGraph::get()) { + int road_sector = QuadGraph::UNKNOWN_SECTOR; + QuadGraph::get()->findRoadSector(xyz, &road_sector); + // If a valid road_sector is not found + if (road_sector == QuadGraph::UNKNOWN_SECTOR) + road_sector = QuadGraph::get()->findOutOfRoadSector(xyz, road_sector); + node_3d = dynamic_cast(QuadGraph::get()->getNode(road_sector)); + } + + Vec3 quad_normal = node_3d ? node_3d->getNormal() : Vec3(0, 1, 0); + if (node_3d || drop) + { + const Material *m; // If raycast is used, increase the start position slightly // in case that the point is too close to the actual surface // (e.g. floating point errors can cause a problem here). - loc += Vec3(0,0.1f,0); + // Only do so for 2d node + if (node_3d == NULL) + loc += Vec3(0.0f, 0.1f, 0.0f); + #ifndef DEBUG - // Avoid unused variable warning in case of non-debug compilation. - setTerrainHeight(&loc); + m_track_mesh->castRay(loc, loc + (-10000 * quad_normal), &hit_point, + &m, &normal); #else - bool drop_success = setTerrainHeight(&loc); - if(!drop_success) + bool drop_success = m_track_mesh->castRay(loc, loc + + (-10000 * quad_normal), &hit_point, &m, &normal); + if (!drop_success) { Log::warn("track", "Item at position (%f,%f,%f) can not be dropped", @@ -2284,48 +2306,10 @@ void Track::itemCommand(const XMLNode *node) #endif } - // Tilt the items according to the track - Vec3 normal(0,1,0); - if (QuadGraph::get()) - { - int road_sector = QuadGraph::UNKNOWN_SECTOR; - QuadGraph::get()->findRoadSector(xyz, &road_sector); - // If a valid road_sector is not found - if (road_sector == QuadGraph::UNKNOWN_SECTOR) - road_sector = QuadGraph::get()->findOutOfRoadSector(xyz, road_sector); - const Vec3& quadnormal = QuadGraph::get()->getNode(road_sector)->getNormal(); - - const Material *m; - Vec3 hit_point; - m_track_mesh->castRay(loc, loc + -1.0f*quadnormal, &hit_point, &m, &normal); - } - ItemManager::get()->newItem(type, loc, normal); + ItemManager::get()->newItem(type, drop ? hit_point : loc, + node_3d ? normal : Vec3(0, 1, 0)); } // itemCommand -// ---------------------------------------------------------------------------- -/** Does a raycast from the given position, and if terrain was found - * adjust the Y position of the given vector to the actual terrain - * height. If no terrain is found, false is returned and the - * y position is not modified. - * \param pos Pointer to the position at which to determine the - * height. If terrain is found, its Y position will be - * set to the actual height. - * \return True if terrain was found and the height was adjusted. - */ -bool Track::setTerrainHeight(Vec3 *pos) const -{ - Vec3 hit_point; - Vec3 normal; - const Material *m; - Vec3 to=*pos+Vec3(0,-10000,0); - if(m_track_mesh->castRay(*pos, to, &hit_point, &m, &normal)) - { - pos->setY(hit_point.getY()); - return true; - } - return false; -} // setTerrainHeight - // ---------------------------------------------------------------------------- std::vector< std::vector > Track::buildHeightMap() diff --git a/src/tracks/track.hpp b/src/tracks/track.hpp index 4c51cf1fd..37f74c285 100644 --- a/src/tracks/track.hpp +++ b/src/tracks/track.hpp @@ -402,7 +402,6 @@ public: void removeCachedData (); void startMusic () const; - bool setTerrainHeight(Vec3 *pos) const; void createPhysicsModel(unsigned int main_track_count); void update(float dt); void reset(); From b0c0af8c462c2267ea670ee6750efa74276687b6 Mon Sep 17 00:00:00 2001 From: Deve Date: Sun, 11 Sep 2016 22:32:27 +0200 Subject: [PATCH 176/350] Check if drivers support BGRA texture format and fallback to RGBA --- src/graphics/central_settings.cpp | 20 ++++++++++++++++++++ src/graphics/central_settings.hpp | 8 ++++++++ src/graphics/graphics_restrictions.cpp | 3 +++ src/graphics/graphics_restrictions.hpp | 3 +++ src/graphics/texture_manager.cpp | 18 ++++++++++++++---- 5 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/graphics/central_settings.cpp b/src/graphics/central_settings.cpp index 13c743d28..3712cf008 100644 --- a/src/graphics/central_settings.cpp +++ b/src/graphics/central_settings.cpp @@ -49,6 +49,10 @@ void CentralVideoSettings::init() hasUBO = false; hasExplicitAttribLocation = false; hasGS = false; + +#if defined(USE_GLES2) + hasBGRA = false; +#endif m_GI_has_artifact = false; m_need_rh_workaround = false; @@ -218,6 +222,15 @@ void CentralVideoSettings::init() hasAtomics = true; hasSSBO = true; } + + if (!GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_TEXTURE_FORMAT_BGRA8888) && + (hasGLExtension("GL_IMG_texture_format_BGRA8888") || + hasGLExtension("GL_EXT_texture_format_BGRA8888"))) + { + hasBGRA = true; + Log::info("GLDriver", "EXT texture format BGRA8888 Present"); + } + #endif } } @@ -337,6 +350,13 @@ bool CentralVideoSettings::isARBMultiDrawIndirectUsable() const return hasMultiDrawIndirect; } +#if defined(USE_GLES2) +bool CentralVideoSettings::isEXTTextureFormatBGRA8888Usable() const +{ + return hasBGRA; +} +#endif + bool CentralVideoSettings::supportsShadows() const { return isARBGeometryShadersUsable() && isARBUniformBufferObjectUsable() && isARBExplicitAttribLocationUsable(); diff --git a/src/graphics/central_settings.hpp b/src/graphics/central_settings.hpp index 059840ebf..3553d7775 100644 --- a/src/graphics/central_settings.hpp +++ b/src/graphics/central_settings.hpp @@ -42,6 +42,10 @@ private: bool hasSSBO; bool hasImageLoadStore; bool hasMultiDrawIndirect; + +#if defined(USE_GLES2) + bool hasBGRA; +#endif bool m_need_rh_workaround; bool m_need_srgb_workaround; @@ -75,6 +79,10 @@ public: bool isARBImageLoadStoreUsable() const; bool isARBMultiDrawIndirectUsable() const; bool isARBExplicitAttribLocationUsable() const; + +#if defined(USE_GLES2) + bool isEXTTextureFormatBGRA8888Usable() const; +#endif // Are all required extensions available for feature support diff --git a/src/graphics/graphics_restrictions.cpp b/src/graphics/graphics_restrictions.cpp index cf624eafd..d660748b5 100644 --- a/src/graphics/graphics_restrictions.cpp +++ b/src/graphics/graphics_restrictions.cpp @@ -57,6 +57,9 @@ namespace GraphicsRestrictions "TextureCompressionS3TC", "AMDVertexShaderLayer", "ExplicitAttribLocation", +#if defined(USE_GLES2) + "TextureFormatBGRA8888", +#endif "DriverRecentEnough", "HighDefinitionTextures", "AdvancedPipeline", diff --git a/src/graphics/graphics_restrictions.hpp b/src/graphics/graphics_restrictions.hpp index 0f0038459..c17acf466 100644 --- a/src/graphics/graphics_restrictions.hpp +++ b/src/graphics/graphics_restrictions.hpp @@ -51,6 +51,9 @@ namespace GraphicsRestrictions GR_EXT_TEXTURE_COMPRESSION_S3TC, GR_AMD_VERTEX_SHADER_LAYER, GR_EXPLICIT_ATTRIB_LOCATION, +#if defined(USE_GLES2) + GR_TEXTURE_FORMAT_BGRA8888, +#endif GR_DRIVER_RECENT_ENOUGH, GR_HIGHDEFINITION_TEXTURES, GR_ADVANCED_PIPELINE, diff --git a/src/graphics/texture_manager.cpp b/src/graphics/texture_manager.cpp index 4ddc71cf2..2fed716c1 100644 --- a/src/graphics/texture_manager.cpp +++ b/src/graphics/texture_manager.cpp @@ -87,10 +87,20 @@ void compressTexture(irr::video::ITexture *tex, bool srgb, bool premul_alpha) memcpy(data, tex->lock(), w * h * 4); tex->unlock(); unsigned internalFormat, Format; - if (tex->hasAlpha()) - Format = GL_BGRA; - else - Format = GL_BGR; + Format = tex->hasAlpha() ? GL_BGRA : GL_BGR; +#if defined(USE_GLES2) + if (!CVS->isEXTTextureFormatBGRA8888Usable()) + { + Format = tex->hasAlpha() ? GL_RGBA : GL_RGB; + + for (unsigned int i = 0; i < w * h; i++) + { + char tmp_val = data[i*4]; + data[i*4] = data[i*4 + 2]; + data[i*4 + 2] = tmp_val; + } + } +#endif if (premul_alpha) { From f4861a62794d9b9704cb6ac649ca482e39bf5d87 Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 12 Sep 2016 09:33:05 +0800 Subject: [PATCH 177/350] Fix bubblegum on upside down track --- src/items/attachment.cpp | 10 ++++------ src/items/powerup.cpp | 10 ++++------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/items/attachment.cpp b/src/items/attachment.cpp index 10070c051..d337da9a2 100644 --- a/src/items/attachment.cpp +++ b/src/items/attachment.cpp @@ -487,19 +487,17 @@ void Attachment::update(float dt) Vec3 hit_point; Vec3 normal; const Material* material_hit; - Vec3 pos = m_kart->getXYZ(); - Vec3 to=pos+Vec3(0, -10000, 0); World* world = World::getWorld(); - world->getTrack()->getTriangleMesh().castRay(pos, to, &hit_point, - &material_hit, &normal); + world->getTrack()->getTriangleMesh().castRay(m_kart->getXYZ(), + m_kart->getTrans().getBasis() * Vec3(0, -10000, 0), &hit_point, + &material_hit, &normal); // This can happen if the kart is 'over nothing' when dropping // the bubble gum if(material_hit) { normal.normalize(); - pos.setY(hit_point.getY()-0.05f); - + Vec3 pos = hit_point + m_kart->getTrans().getBasis() * Vec3(0, -0.05f, 0); ItemManager::get()->newItem(Item::ITEM_BUBBLEGUM, pos, normal, m_kart); } } diff --git a/src/items/powerup.cpp b/src/items/powerup.cpp index 930c8c1bb..ec31bd4f1 100644 --- a/src/items/powerup.cpp +++ b/src/items/powerup.cpp @@ -232,10 +232,9 @@ void Powerup::use() Vec3 hit_point; Vec3 normal; const Material* material_hit; - Vec3 pos = m_owner->getXYZ(); - Vec3 to=pos+Vec3(0, -10000, 0); - world->getTrack()->getTriangleMesh().castRay(pos, to, &hit_point, - &material_hit, &normal); + world->getTrack()->getTriangleMesh().castRay(m_owner->getXYZ(), + m_owner->getTrans().getBasis() * Vec3(0, -10000, 0), &hit_point, + &material_hit, &normal); // This can happen if the kart is 'over nothing' when dropping // the bubble gum if(!material_hit) @@ -245,8 +244,7 @@ void Powerup::use() Powerup::adjustSound(); m_sound_use->play(); - pos.setY(hit_point.getY()-0.05f); - + Vec3 pos = hit_point + m_owner->getTrans().getBasis() * Vec3(0, -0.05f, 0); ItemManager::get()->newItem(Item::ITEM_BUBBLEGUM, pos, normal, m_owner); } else // if the kart is looking forward, use the bubblegum as a shield From cc54c44ec047ce80c8b921b637b5955611006035 Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 12 Sep 2016 10:43:49 +0800 Subject: [PATCH 178/350] Fix rotation of items on upside down surface --- src/items/item.cpp | 38 +++++++++++++++++++++++--------------- src/items/item.hpp | 5 ++++- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/items/item.cpp b/src/items/item.cpp index 9b6135f68..75f72d075 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -41,16 +41,15 @@ Item::Item(ItemType type, const Vec3& xyz, const Vec3& normal, m_distance_2 = 1.2f; initItem(type, xyz); - //* Rotate item depending on the normal */ - if (normal.angle(Vec3(0, 1, 0))!= 0) + if (-normal.cross(Vec3(0, 1, 0)).length() == 0) { - m_original_hpr.setHPR(btQuaternion(-normal.cross(Vec3(0, 1, 0)), - normal.angle(Vec3(0, 1, 0)))); + Vec3 axis(0, 0, 1); + m_original_rotation = btQuaternion(axis, normal.angle(Vec3(0, 1, 0))); } else { - // Sets heading to 0, and sets pitch and roll depending on the normal. */ - m_original_hpr = Vec3(0, normal); + Vec3 axis(-normal.cross(Vec3(0, 1, 0))); + m_original_rotation = btQuaternion(axis, normal.angle(Vec3(0, 1, 0))); } m_original_mesh = mesh; @@ -88,7 +87,9 @@ Item::Item(ItemType type, const Vec3& xyz, const Vec3& normal, World::getWorld()->getTrack()->adjustForFog(m_node); m_node->setAutomaticCulling(scene::EAC_FRUSTUM_BOX); m_node->setPosition(xyz.toIrrVector()); - m_node->setRotation(m_original_hpr.toIrrHPR()); + Vec3 hpr; + hpr.setHPR(m_original_rotation); + m_node->setRotation(hpr.toIrrHPR()); m_node->grab(); } // Item(type, xyz, normal, mesh, lowres_mesh) @@ -102,8 +103,8 @@ Item::Item(const Vec3& xyz, float distance, TriggerItemListener* trigger) { m_distance_2 = distance*distance; initItem(ITEM_TRIGGER, xyz); - // Sets heading to 0, and sets pitch and roll depending on the normal. */ - m_original_hpr = Vec3(0, 0, 0); + m_original_rotation = btQuaternion(0, 0, 0, 1); + m_rotation_angle = 0.0f; m_original_mesh = NULL; m_original_lowmesh = NULL; m_node = NULL; @@ -235,7 +236,9 @@ void Item::switchBack() } World::getWorld()->getTrack()->adjustForFog(m_node); - m_node->setRotation(m_original_hpr.toIrrHPR()); + Vec3 hpr; + hpr.setHPR(m_original_rotation); + m_node->setRotation(hpr.toIrrHPR()); } // switchBack //----------------------------------------------------------------------------- @@ -332,12 +335,17 @@ void Item::update(float dt) if(!m_rotate || m_node == NULL) return; // have it rotate - Vec3 rotation(0, dt*M_PI, 0); - core::vector3df r = m_node->getRotation(); - r.Y += dt*180.0f; - if(r.Y>360.0f) r.Y -= 360.0f; + m_rotation_angle += dt * M_PI ; + if (m_rotation_angle > M_PI * 2) m_rotation_angle -= M_PI * 2; - m_node->setRotation(r); + btMatrix3x3 m; + m.setRotation(m_original_rotation); + btQuaternion r = btQuaternion(m.getColumn(1), m_rotation_angle) * + m_original_rotation; + + Vec3 hpr; + hpr.setHPR(r); + m_node->setRotation(hpr.toIrrHPR()); return; } // not m_collected } // update diff --git a/src/items/item.hpp b/src/items/item.hpp index fa586fb4d..0654d2ea7 100644 --- a/src/items/item.hpp +++ b/src/items/item.hpp @@ -101,7 +101,10 @@ private: * (bubble gums don't rotate, but it will be replaced with * a nitro which rotates, and so overwrites the original * rotation). */ - Vec3 m_original_hpr; + btQuaternion m_original_rotation; + + /** Used when rotating the item */ + float m_rotation_angle; /** True if item was collected & is not displayed. */ bool m_collected; From 7544dc33158a48ad834d4645ba0008fa80dd8d44 Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 12 Sep 2016 11:35:34 +0800 Subject: [PATCH 179/350] Fix uninitialized value --- src/items/item.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/items/item.cpp b/src/items/item.cpp index 75f72d075..83a0231aa 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -52,6 +52,7 @@ Item::Item(ItemType type, const Vec3& xyz, const Vec3& normal, m_original_rotation = btQuaternion(axis, normal.angle(Vec3(0, 1, 0))); } + m_rotation_angle = 0.0f; m_original_mesh = mesh; m_original_lowmesh = lowres_mesh; m_listener = NULL; From b699018879d6d055a9821b35fd9ffc9a95c1a71a Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 12 Sep 2016 13:04:05 +0800 Subject: [PATCH 180/350] Only do custom direction of raycast if item is on quad graph --- src/tracks/track.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index c18b4c4df..854bcb6e6 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -2273,10 +2273,12 @@ void Track::itemCommand(const XMLNode *node) { int road_sector = QuadGraph::UNKNOWN_SECTOR; QuadGraph::get()->findRoadSector(xyz, &road_sector); - // If a valid road_sector is not found - if (road_sector == QuadGraph::UNKNOWN_SECTOR) - road_sector = QuadGraph::get()->findOutOfRoadSector(xyz, road_sector); - node_3d = dynamic_cast(QuadGraph::get()->getNode(road_sector)); + // Only do custom direction of raycast if item is on quad graph + if (road_sector != QuadGraph::UNKNOWN_SECTOR) + { + node_3d = + dynamic_cast(QuadGraph::get()->getNode(road_sector)); + } } Vec3 quad_normal = node_3d ? node_3d->getNormal() : Vec3(0, 1, 0); From 3a9b2b9872e5e555392ebc253155afcc10720bcd Mon Sep 17 00:00:00 2001 From: Deve Date: Mon, 12 Sep 2016 21:39:52 +0200 Subject: [PATCH 181/350] Fixed advanced lighting on android --- data/shaders/IBL.frag | 5 +++++ data/shaders/degraded_ibl.frag | 5 +++++ data/shaders/pointlight.frag | 5 +++++ data/shaders/sunlight.frag | 5 +++++ data/shaders/sunlightshadow.frag | 5 +++++ data/shaders/sunlightshadowesm.frag | 5 +++++ 6 files changed, 30 insertions(+) diff --git a/data/shaders/IBL.frag b/data/shaders/IBL.frag index cadb675c1..7d3eb2efa 100644 --- a/data/shaders/IBL.frag +++ b/data/shaders/IBL.frag @@ -1,8 +1,13 @@ uniform sampler2D ntex; uniform sampler2D dtex; +#ifdef GL_ES +layout (location = 0) out vec4 Diff; +layout (location = 1) out vec4 Spec; +#else out vec4 Diff; out vec4 Spec; +#endif #stk_include "utils/decodeNormal.frag" #stk_include "utils/getPosFromUVDepth.frag" diff --git a/data/shaders/degraded_ibl.frag b/data/shaders/degraded_ibl.frag index 05e10c942..be359f04d 100644 --- a/data/shaders/degraded_ibl.frag +++ b/data/shaders/degraded_ibl.frag @@ -1,7 +1,12 @@ uniform sampler2D ntex; +#ifdef GL_ES +layout (location = 0) out vec4 Diff; +layout (location = 1) out vec4 Spec; +#else out vec4 Diff; out vec4 Spec; +#endif #stk_include "utils/decodeNormal.frag" #stk_include "utils/getPosFromUVDepth.frag" diff --git a/data/shaders/pointlight.frag b/data/shaders/pointlight.frag index b49e7b816..294065be6 100644 --- a/data/shaders/pointlight.frag +++ b/data/shaders/pointlight.frag @@ -6,8 +6,13 @@ flat in float energy; flat in vec3 col; flat in float radius; +#ifdef GL_ES +layout (location = 0) out vec4 Diff; +layout (location = 1) out vec4 Spec; +#else out vec4 Diff; out vec4 Spec; +#endif #stk_include "utils/decodeNormal.frag" #stk_include "utils/SpecularBRDF.frag" diff --git a/data/shaders/sunlight.frag b/data/shaders/sunlight.frag index 32453ec56..36da04623 100644 --- a/data/shaders/sunlight.frag +++ b/data/shaders/sunlight.frag @@ -1,8 +1,13 @@ uniform sampler2D ntex; uniform sampler2D dtex; +#ifdef GL_ES +layout (location = 0) out vec4 Diff; +layout (location = 1) out vec4 Spec; +#else out vec4 Diff; out vec4 Spec; +#endif #stk_include "utils/decodeNormal.frag" #stk_include "utils/SpecularBRDF.frag" diff --git a/data/shaders/sunlightshadow.frag b/data/shaders/sunlightshadow.frag index ed35dff62..df86d2258 100644 --- a/data/shaders/sunlightshadow.frag +++ b/data/shaders/sunlightshadow.frag @@ -9,8 +9,13 @@ uniform float splitmax; uniform float shadow_res; in vec2 uv; +#ifdef GL_ES +layout (location = 0) out vec4 Diff; +layout (location = 1) out vec4 Spec; +#else out vec4 Diff; out vec4 Spec; +#endif #stk_include "utils/decodeNormal.frag" #stk_include "utils/SpecularBRDF.frag" diff --git a/data/shaders/sunlightshadowesm.frag b/data/shaders/sunlightshadowesm.frag index 8ac2a4235..4a0ca1c7d 100644 --- a/data/shaders/sunlightshadowesm.frag +++ b/data/shaders/sunlightshadowesm.frag @@ -8,8 +8,13 @@ uniform float split2; uniform float splitmax; in vec2 uv; +#ifdef GL_ES +layout (location = 0) out vec4 Diff; +layout (location = 1) out vec4 Spec; +#else out vec4 Diff; out vec4 Spec; +#endif #stk_include "utils/decodeNormal.frag" #stk_include "utils/SpecularBRDF.frag" From f5d43aaa2e57728f6e2b39e3a2644f8459dc425a Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 13 Sep 2016 07:43:19 +0800 Subject: [PATCH 182/350] Use btAsin in setHPR The following quaternion calculated by shortestArcQuat in rescue animation leads to nan in asinf: 0.710828841, -0.00974362344, -0.703500867, 0.00481829932 -2.0f * (X * Z - Y * W) equals 1.00004351 with above figures With btAsin it will: if (xbtScalar(1)) x=btScalar(1); return asin(x); --- src/utils/vec3.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/vec3.cpp b/src/utils/vec3.cpp index d7b5cfdef..d3c2001bd 100644 --- a/src/utils/vec3.cpp +++ b/src/utils/vec3.cpp @@ -30,7 +30,7 @@ void Vec3::setHPR(const btQuaternion& q) float ZSquared = Z * Z; setX(atan2f(2.0f * (Y * Z + X * W), -XSquared - YSquared + ZSquared + WSquared)); - setY(asinf(-2.0f * (X * Z - Y * W))); + setY(btAsin(-2.0f * (X * Z - Y * W))); setZ(atan2f(2.0f * (X * Y + Z * W), XSquared - YSquared - ZSquared + WSquared)); } // setHPR(btQuaternion) From f8b48a03131dc5b686bb2a9061c66bc236ed5bfe Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 13 Sep 2016 09:55:51 +1000 Subject: [PATCH 183/350] Added zipper speed to state of kart. --- src/karts/kart_rewinder.cpp | 3 +++ src/physics/btKart.cpp | 14 +++++++------- src/physics/btKart.hpp | 7 +++++-- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/karts/kart_rewinder.cpp b/src/karts/kart_rewinder.cpp index 65a863ae8..977d59c5c 100644 --- a/src/karts/kart_rewinder.cpp +++ b/src/karts/kart_rewinder.cpp @@ -26,6 +26,7 @@ #include "modes/world.hpp" #include "network/rewind_manager.hpp" #include "network/network_string.hpp" +#include "physics/btKart.hpp" #include "utils/vec3.hpp" #include @@ -73,6 +74,7 @@ BareNetworkString* KartRewinder::saveState() const buffer->add(body->getLinearVelocity()); buffer->add(body->getAngularVelocity()); buffer->addUInt8(m_has_started); // necessary for startup speed boost + buffer->addFloat(m_vehicle->getZipperSpeed()); // 2) Steering and other player controls // ------------------------------------- @@ -113,6 +115,7 @@ void KartRewinder::rewindToState(BareNetworkString *buffer) body->setLinearVelocity(buffer->getVec3()); body->setAngularVelocity(buffer->getVec3()); m_has_started = buffer->getUInt8()!=0; // necessary for startup speed boost + m_vehicle->instantSpeedIncreaseTo(buffer->getFloat()); // 2) Steering and other controls // ------------------------------ diff --git a/src/physics/btKart.cpp b/src/physics/btKart.cpp index 63037b618..3fc9e0e06 100644 --- a/src/physics/btKart.cpp +++ b/src/physics/btKart.cpp @@ -117,7 +117,7 @@ void btKart::reset() } m_visual_wheels_touch_ground = false; m_zipper_active = false; - m_zipper_velocity = btScalar(0); + m_zipper_speed = btScalar(0); m_skid_angular_velocity = 0; m_is_skidding = false; m_allow_sliding = false; @@ -810,7 +810,7 @@ void btKart::updateFriction(btScalar timeStep) (btRigidBody*) wheelInfo.m_raycastInfo.m_groundObject; if(!groundObject) continue; - if(m_zipper_active && m_zipper_velocity > 0) + if(m_zipper_active && m_zipper_speed > 0) { if (wheel==2 || wheel==3) { @@ -818,7 +818,7 @@ void btKart::updateFriction(btScalar timeStep) // reached. So compute the impulse to accelerate the // kart up to that speed: m_forwardImpulse[wheel] = - 0.5f*(m_zipper_velocity - + 0.5f*(m_zipper_speed - getRigidBody()->getLinearVelocity().length()) / m_chassisBody->getInvMass(); } @@ -882,8 +882,8 @@ void btKart::updateFriction(btScalar timeStep) } // for (int wheel=0; wheel Date: Tue, 13 Sep 2016 09:58:42 +1000 Subject: [PATCH 184/350] Make rewind work with history replay (when replaying kart controls), which allows to have reproducible test cases. --- src/karts/abstract_kart.hpp | 3 -- src/karts/controller/kart_control.cpp | 53 ++++++++++++++++++--------- src/karts/controller/kart_control.hpp | 1 + src/race/history.cpp | 18 ++++++++- 4 files changed, 53 insertions(+), 22 deletions(-) diff --git a/src/karts/abstract_kart.hpp b/src/karts/abstract_kart.hpp index f7105603a..7fba6d672 100644 --- a/src/karts/abstract_kart.hpp +++ b/src/karts/abstract_kart.hpp @@ -116,9 +116,6 @@ public: // ------------------------------------------------------------------------ /** Returns all controls of this kart - const version. */ const KartControl& getControls() const { return m_controls; } - // ------------------------------------------------------------------------ - /** Sets the kart controls. Used e.g. by replaying history. */ - void setControls(const KartControl &c) { m_controls = c; } // ======================================================================== // Access to the kart properties. diff --git a/src/karts/controller/kart_control.cpp b/src/karts/controller/kart_control.cpp index 5532949c1..8075a68f2 100644 --- a/src/karts/controller/kart_control.cpp +++ b/src/karts/controller/kart_control.cpp @@ -46,14 +46,31 @@ void KartControl::rewind(BareNetworkString *buffer) } } // rewind +// ------------------------------------------------------------------------ +/** Sets this KartControl form the given value (basically a copy). This + * function uses the explicit setSteer() etc function, which means that + * rewind information will be collected. + */ +void KartControl::set(const KartControl &c) +{ + setAccel(c.getAccel()); + setBrake(c.getBrake()); + setFire(c.getFire()); + setLookBack(c.getLookBack()); + setNitro(c.getNitro()); + setRescue(c.getRescue()); + setSkidControl(c.getSkidControl()); + setSteer(c.getSteer()); +} // set + // ------------------------------------------------------------------------ /** Sets the current steering value. */ void KartControl::setSteer(float f) { float old_steer = m_steer; m_steer = f; - RewindManager *re = RewindManager::get(); - if (re->isEnabled() && !re->isRewinding() && old_steer != m_steer) + if (RewindManager::isEnabled() && !RewindManager::get()->isRewinding() && + old_steer != m_steer ) { // Save full status BareNetworkString *buffer = new BareNetworkString(getLength()); @@ -68,8 +85,8 @@ void KartControl::setAccel(float f) { float old_accel = m_accel; m_accel = f; - RewindManager *re = RewindManager::get(); - if (re->isEnabled() && !re->isRewinding() && old_accel != m_accel) + if (RewindManager::isEnabled() && !RewindManager::get()->isRewinding() && + old_accel != m_accel ) { BareNetworkString *buffer = new BareNetworkString(getLength()); copyToBuffer(buffer); @@ -81,10 +98,10 @@ void KartControl::setAccel(float f) /** Sets if the kart is braking. */ void KartControl::setBrake(bool b) { - bool old_brake = m_brake; - m_brake = b; - RewindManager *re = RewindManager::get(); - if (re->isEnabled() && !re->isRewinding() && old_brake != m_brake) + bool old_brake = m_brake; + m_brake = b; + if (RewindManager::isEnabled() && !RewindManager::get()->isRewinding() && + old_brake != m_brake ) { // Only store the buttons in this case BareNetworkString *buffer = new BareNetworkString(1); @@ -98,8 +115,8 @@ void KartControl::setNitro(bool b) { bool old_nitro = m_nitro; m_nitro = b; - RewindManager *re = RewindManager::get(); - if (re->isEnabled() && !re->isRewinding() && old_nitro != m_nitro) + if (RewindManager::isEnabled() && !RewindManager::get()->isRewinding() && + old_nitro != m_nitro ) { BareNetworkString *buffer = new BareNetworkString(1); buffer->addUInt8(getButtonsCompressed()); @@ -113,8 +130,8 @@ void KartControl::setSkidControl(SkidControl sc) { SkidControl old_skid = m_skid; m_skid = sc; - RewindManager *re = RewindManager::get(); - if (re->isEnabled() && !re->isRewinding() && old_skid != m_skid) + if (RewindManager::isEnabled() && !RewindManager::get()->isRewinding() && + old_skid != m_skid ) { BareNetworkString *buffer = new BareNetworkString(1); buffer->addUInt8(getButtonsCompressed()); @@ -128,8 +145,8 @@ void KartControl::setRescue(bool b) { bool old_rescue = m_rescue; m_rescue = b; - RewindManager *re = RewindManager::get(); - if (re->isEnabled() && !re->isRewinding() && old_rescue != m_rescue) + if (RewindManager::isEnabled() && !RewindManager::get()->isRewinding() && + old_rescue != m_rescue) { BareNetworkString *buffer = new BareNetworkString(1); buffer->addUInt8(getButtonsCompressed()); @@ -143,8 +160,8 @@ void KartControl::setFire(bool b) { bool old_fire = m_fire; m_fire = b; - RewindManager *re = RewindManager::get(); - if (re->isEnabled() && !re->isRewinding() && old_fire != m_fire) + if (RewindManager::isEnabled() && !RewindManager::get()->isRewinding() && + old_fire != m_fire ) { BareNetworkString *buffer = new BareNetworkString(1); buffer->addUInt8(getButtonsCompressed()); @@ -158,8 +175,8 @@ void KartControl::setLookBack(bool b) { bool old_look = m_look_back; m_look_back = b; - RewindManager *re = RewindManager::get(); - if (re->isEnabled() && !re->isRewinding() && old_look != m_look_back) + if (RewindManager::isEnabled() && !RewindManager::get()->isRewinding() && + old_look != m_look_back) { BareNetworkString *buffer = new BareNetworkString(1); buffer->addUInt8(getButtonsCompressed()); diff --git a/src/karts/controller/kart_control.hpp b/src/karts/controller/kart_control.hpp index 438132cd5..e4a2afec4 100644 --- a/src/karts/controller/kart_control.hpp +++ b/src/karts/controller/kart_control.hpp @@ -61,6 +61,7 @@ public: void setRescue(bool b); void setFire(bool b); void setLookBack(bool b); + void set(const KartControl &c); // ------------------------------------------------------------------------ KartControl() diff --git a/src/race/history.cpp b/src/race/history.cpp index 82f8fe79f..b3d5d479d 100644 --- a/src/race/history.cpp +++ b/src/race/history.cpp @@ -23,6 +23,7 @@ #include "io/file_manager.hpp" #include "modes/world.hpp" #include "karts/abstract_kart.hpp" +#include "network/rewind_manager.hpp" #include "physics/physics.hpp" #include "race/race_manager.hpp" #include "tracks/track.hpp" @@ -131,9 +132,17 @@ void History::updateReplay(float dt) { Log::info("History", "Replay finished"); m_current = 0; + // This is useful to use a reproducable rewind problem: + // replay it with history, for debugging only +#undef DO_REWIND_AT_END_OF_HISTORY +#ifdef DO_REWIND_AT_END_OF_HISTORY + RewindManager::get()->rewindTo(5.0f); + exit(-1); +#else // Note that for physics replay all physics parameters // need to be reset, e.g. velocity, ... world->reset(); +#endif } unsigned int num_karts = world->getNumKarts(); for(unsigned k=0; ksetControls(m_all_controls[index]); + kart->getControls().set(m_all_controls[index]); } } } // updateReplay @@ -314,6 +323,11 @@ void History::Load() sscanf(s, "delta: %f\n",&m_all_deltas[i]); } + // We need to disable the rewind manager here (otherwise setting the + // KartControl data would access the rewind manager). + bool rewind_manager_was_enabled = RewindManager::isEnabled(); + RewindManager::setEnable(false); + for(int i=0; i Date: Tue, 13 Sep 2016 09:26:05 +0800 Subject: [PATCH 185/350] Simplify code --- src/items/item.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/items/item.cpp b/src/items/item.cpp index 83a0231aa..01ed0d5d5 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -41,17 +41,11 @@ Item::Item(ItemType type, const Vec3& xyz, const Vec3& normal, m_distance_2 = 1.2f; initItem(type, xyz); - if (-normal.cross(Vec3(0, 1, 0)).length() == 0) - { - Vec3 axis(0, 0, 1); - m_original_rotation = btQuaternion(axis, normal.angle(Vec3(0, 1, 0))); - } - else - { - Vec3 axis(-normal.cross(Vec3(0, 1, 0))); - m_original_rotation = btQuaternion(axis, normal.angle(Vec3(0, 1, 0))); - } + Vec3 axis = -normal.cross(Vec3(0, 1, 0)); + if (axis.length() == 0) + axis = Vec3(0, 0, 1); + m_original_rotation = btQuaternion(axis, normal.angle(Vec3(0, 1, 0))); m_rotation_angle = 0.0f; m_original_mesh = mesh; m_original_lowmesh = lowres_mesh; From 3f867221811fec04f30efdf8a243508737efac2f Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 13 Sep 2016 11:48:36 +0800 Subject: [PATCH 186/350] Fix skid mark on wall surface --- src/graphics/skid_marks.cpp | 24 +++++++++++++----------- src/graphics/skid_marks.hpp | 5 +++-- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/graphics/skid_marks.cpp b/src/graphics/skid_marks.cpp index 9d45af521..48dcf1a74 100644 --- a/src/graphics/skid_marks.cpp +++ b/src/graphics/skid_marks.cpp @@ -153,9 +153,9 @@ void SkidMarks::update(float dt, bool force_skid_marks, float distance = (newPoint - start).length(); m_left [m_current]->add(raycast_left-delta, raycast_left+delta, - distance); + m_kart.getNormal(), distance); m_right[m_current]->add(raycast_right-delta, raycast_right+delta, - distance); + m_kart.getNormal(), distance); // Adjust the boundary box of the mesh to include the // adjusted aabb of its buffers. core::aabbox3df aabb=m_nodes[m_current]->getMesh() @@ -180,14 +180,16 @@ void SkidMarks::update(float dt, bool force_skid_marks, delta *= m_width*0.5f; SkidMarkQuads *smq_left = - new SkidMarkQuads(raycast_left-delta, raycast_left+delta , - m_material, m_avoid_z_fighting, custom_color); + new SkidMarkQuads(raycast_left-delta, raycast_left+delta, + m_kart.getNormal(), m_material, m_avoid_z_fighting, + custom_color); scene::SMesh *new_mesh = new scene::SMesh(); new_mesh->addMeshBuffer(smq_left); SkidMarkQuads *smq_right = new SkidMarkQuads(raycast_right-delta, raycast_right+delta, - m_material, m_avoid_z_fighting, custom_color); + m_kart.getNormal(), m_material, m_avoid_z_fighting, + custom_color); new_mesh->addMeshBuffer(smq_right); scene::IMeshSceneNode *new_node = irr_driver->addMesh(new_mesh, "skidmark"); if (STKMeshSceneNode* stkm = dynamic_cast(new_node)) @@ -233,6 +235,7 @@ void SkidMarks::update(float dt, bool force_skid_marks, //============================================================================= SkidMarks::SkidMarkQuads::SkidMarkQuads(const Vec3 &left, const Vec3 &right, + const Vec3 &normal, video::SMaterial *material, float z_offset, video::SColor* custom_color) @@ -250,7 +253,7 @@ SkidMarks::SkidMarkQuads::SkidMarkQuads(const Vec3 &left, Material = *material; m_aabb = core::aabbox3df(left.toIrrVector()); - add(left, right, 0.0f); + add(left, right, normal, 0.0f); } // SkidMarkQuads @@ -261,6 +264,7 @@ SkidMarks::SkidMarkQuads::SkidMarkQuads(const Vec3 &left, */ void SkidMarks::SkidMarkQuads::add(const Vec3 &left, const Vec3 &right, + const Vec3 &normal, float distance) { // The skid marks must be raised slightly higher, otherwise it blends @@ -280,13 +284,11 @@ void SkidMarks::SkidMarkQuads::add(const Vec3 &left, Vertices[n - 2].Color.setAlpha(m_start_alpha); } - v.Pos = left.toIrrVector(); - v.Pos.Y += m_z_offset; - v.Normal = core::vector3df(0, 1, 0); + v.Pos = Vec3(left + normal * m_z_offset).toIrrVector(); + v.Normal = normal.toIrrVector(); v.TCoords = core::vector2df(0.0f, distance*0.5f); Vertices.push_back(v); - v.Pos = right.toIrrVector(); - v.Pos.Y += m_z_offset; + v.Pos = Vec3(right + normal * m_z_offset).toIrrVector(); v.TCoords = core::vector2df(1.0f, distance*0.5f); Vertices.push_back(v); // Out of the box Irrlicht only supports triangle meshes and not diff --git a/src/graphics/skid_marks.hpp b/src/graphics/skid_marks.hpp index 0f92b491c..5c00cbd8a 100644 --- a/src/graphics/skid_marks.hpp +++ b/src/graphics/skid_marks.hpp @@ -85,10 +85,11 @@ private: public: SkidMarkQuads (const Vec3 &left, const Vec3 &right, - video::SMaterial *material, float z_offset, - video::SColor* custom_color = NULL); + const Vec3 &normal, video::SMaterial *material, + float z_offset, video::SColor* custom_color = NULL); void add (const Vec3 &left, const Vec3 &right, + const Vec3 &normal, float distance); void fade (float f); /** Returns the aabb of this skid mark quads. */ From 4574686c1db0e4cad579bfcec34ec490965ac269 Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 13 Sep 2016 12:49:30 +0800 Subject: [PATCH 187/350] Make angle computation work in 3D --- src/karts/controller/ai_base_controller.cpp | 16 +++++++--------- src/karts/controller/skidding_ai.cpp | 16 ++++++++++------ src/karts/controller/test_ai.cpp | 16 ++++++++++------ 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/karts/controller/ai_base_controller.cpp b/src/karts/controller/ai_base_controller.cpp index ec93e1599..b44c91453 100644 --- a/src/karts/controller/ai_base_controller.cpp +++ b/src/karts/controller/ai_base_controller.cpp @@ -86,9 +86,7 @@ float AIBaseController::steerToPoint(const Vec3 &point) // First translate and rotate the point the AI is aiming // at into the kart's local coordinate system. - Vec3 lc; - btTransform trans = m_kart->getTrans().inverse(); - lc = trans(point); + Vec3 lc = m_kart->getTrans().inverse()(point); // The point the kart is aiming at can be reached 'incorrectly' if the // point is below the y=x line: Instead of aiming at that point directly @@ -287,10 +285,10 @@ void AIBaseController::checkPosition(const Vec3 &point, posData *pos_data, Vec3 *lc, bool use_front_xyz) const { // Convert to local coordinates from the point of view of current kart - btQuaternion q(btVector3(0, 1, 0), -m_kart->getHeading()); - Vec3 p = point - - (use_front_xyz ? m_kart->getFrontXYZ() : m_kart->getXYZ()); - Vec3 local_coordinates = quatRotate(q, p); + btTransform t; + t.setBasis(m_kart->getTrans().getBasis()); + t.setOrigin(use_front_xyz ? m_kart->getFrontXYZ() : m_kart->getXYZ()); + Vec3 local_coordinates = t.inverse()(point); // Save local coordinates for later use if needed if (lc) *lc = local_coordinates; @@ -308,8 +306,8 @@ void AIBaseController::checkPosition(const Vec3 &point, posData *pos_data, else pos_data->behind = false; - pos_data->angle = atan2(fabsf(local_coordinates.getX()), + pos_data->angle = atan2f(fabsf(local_coordinates.getX()), fabsf(local_coordinates.getZ())); - pos_data->distance = p.length(); + pos_data->distance = local_coordinates.length(); } // checkPosition diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index 703995587..1e443a56e 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -1330,15 +1330,19 @@ void SkiddingAI::handleItems(const float dt) bool straight_ahead = false; if (m_kart_behind) { - posData behind_pos = {0}; - checkPosition(m_kart_behind->getXYZ(), &behind_pos); - if (behind_pos.angle < 0.2f) straight_behind = true; + Vec3 behind_lc = m_kart->getTrans().inverse() + (m_kart_behind->getXYZ()); + const float abs_angle = + atan2f(fabsf(behind_lc.x()), fabsf(behind_lc.z())); + if (abs_angle < 0.2f) straight_behind = true; } if (m_kart_ahead) { - posData ahead_pos = {0}; - checkPosition(m_kart_ahead->getXYZ(), &ahead_pos); - if (ahead_pos.angle < 0.2f) straight_ahead = true; + Vec3 ahead_lc = m_kart->getTrans().inverse() + (m_kart_ahead->getXYZ()); + const float abs_angle = + atan2f(fabsf(ahead_lc.x()), fabsf(ahead_lc.z())); + if (abs_angle < 0.2f) straight_ahead = true; } // Bowling balls are slower, so only fire on closer karts - but when diff --git a/src/karts/controller/test_ai.cpp b/src/karts/controller/test_ai.cpp index 6c8334188..e2004a484 100644 --- a/src/karts/controller/test_ai.cpp +++ b/src/karts/controller/test_ai.cpp @@ -1336,15 +1336,19 @@ void SkiddingAI::handleItems(const float dt) bool straight_ahead = false; if (m_kart_behind) { - posData behind_pos = {0}; - checkPosition(m_kart_behind->getXYZ(), &behind_pos); - if (behind_pos.angle < 0.2f) straight_behind = true; + Vec3 behind_lc = m_kart->getTrans().inverse() + (m_kart_behind->getXYZ()); + const float abs_angle = + atan2f(fabsf(behind_lc.x()), fabsf(behind_lc.z())); + if (abs_angle < 0.2f) straight_behind = true; } if (m_kart_ahead) { - posData ahead_pos = {0}; - checkPosition(m_kart_ahead->getXYZ(), &ahead_pos); - if (ahead_pos.angle < 0.2f) straight_ahead = true; + Vec3 ahead_lc = m_kart->getTrans().inverse() + (m_kart_ahead->getXYZ()); + const float abs_angle = + atan2f(fabsf(ahead_lc.x()), fabsf(ahead_lc.z())); + if (abs_angle < 0.2f) straight_ahead = true; } // Bowling balls are slower, so only fire on closer karts - but when From 061f1871429c667913dbe35ed1b89dbcae276cae Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 13 Sep 2016 15:07:07 +0800 Subject: [PATCH 188/350] Make slipstream work in upside down track --- src/graphics/slip_stream.cpp | 19 ++++++------------- src/graphics/slip_stream.hpp | 7 +------ src/tracks/quad.cpp | 23 ----------------------- src/tracks/quad.hpp | 4 ---- 4 files changed, 7 insertions(+), 46 deletions(-) diff --git a/src/graphics/slip_stream.cpp b/src/graphics/slip_stream.cpp index dea3781c6..7b005f2f4 100644 --- a/src/graphics/slip_stream.cpp +++ b/src/graphics/slip_stream.cpp @@ -77,7 +77,6 @@ SlipStream::SlipStream(AbstractKart* kart) : MovingTexture(0, 0), m_kart(kart) p[1]=Vec3(-ew*0.5f, 0, -kl*0.5f-length); p[2]=Vec3( ew*0.5f, 0, -kl*0.5f-length); p[3]=Vec3( kw*0.5f, 0, -kl*0.5f ); - m_slipstream_original_quad = new Quad(p[0], p[1], p[2], p[3]); m_slipstream_quad = new Quad(p[0], p[1], p[2], p[3]); if(UserConfigParams::m_slipstream_debug) { @@ -127,7 +126,6 @@ SlipStream::~SlipStream() m_debug_node->drop(); m_debug_mesh->drop(); } - delete m_slipstream_original_quad; delete m_slipstream_quad; } // ~SlipStream @@ -369,12 +367,6 @@ void SlipStream::update(float dt) MovingTexture::update(dt); - // Update this karts slipstream quad (even for low level AI which don't - // use slipstream, since even then player karts can get slipstream, - // and so have to compare with the modified slipstream quad. - m_slipstream_original_quad->transform(m_kart->getTrans(), - m_slipstream_quad); - if(m_slipstream_mode==SS_USE) { m_slipstream_time -= dt; @@ -418,12 +410,13 @@ void SlipStream::update(float dt) m_target_kart->getKartAnimation() || m_target_kart->isEliminated() ) continue; - float diff = fabsf(m_target_kart->getXYZ().getY() - - m_kart->getXYZ().getY() ); + // Transform this kart location into target kart point of view + Vec3 lc = m_target_kart->getTrans().inverse()(m_kart->getXYZ()); + // If the kart is 'on top' of this kart (e.g. up on a bridge), // don't consider it for slipstreaming. + if (fabsf(lc.y()) > 6.0f) continue; - if(diff>6.0f) continue; // If the kart we are testing against is too slow, no need to test // slipstreaming. Note: We compare the speed of the other kart // against the minimum slipstream speed kart of this kart - not @@ -447,7 +440,7 @@ void SlipStream::update(float dt) float l = kp->getSlipstreamLength() + 0.5f*( m_target_kart->getKartLength() +m_kart->getKartLength() ); - if(delta.length2_2d() > l*l) + if(delta.length2() > l*l) { if(UserConfigParams::m_slipstream_debug && m_kart->getController()->isLocalPlayerController()) @@ -457,7 +450,7 @@ void SlipStream::update(float dt) } // Real test: if in slipstream quad of other kart if(m_target_kart->getSlipstream()->m_slipstream_quad - ->pointInside(m_kart->getXYZ())) + ->pointInside(lc)) { is_sstreaming = true; break; diff --git a/src/graphics/slip_stream.hpp b/src/graphics/slip_stream.hpp index c77675b3c..c99df04e1 100644 --- a/src/graphics/slip_stream.hpp +++ b/src/graphics/slip_stream.hpp @@ -66,14 +66,9 @@ private: * 'slipstream credits', or the kart is using accumulated credits. */ enum {SS_NONE, SS_COLLECT, SS_USE} m_slipstream_mode; - /** The quad inside which another kart is considered to be slipstreaming. - * This value is current area, i.e. takes the kart position into account. */ + /** This is slipstream area if the kart is at 0,0,0 without rotation. */ Quad *m_slipstream_quad; - /** This is slipstream area if the kart is at 0,0,0 without rotation. From - * this value m_slipstream_area is computed by applying the kart transform. */ - Quad *m_slipstream_original_quad; - /** The kart from which this kart gets slipstream. Used by the AI to ** overtake the right kart. */ AbstractKart* m_target_kart; diff --git a/src/tracks/quad.cpp b/src/tracks/quad.cpp index d6a697ec7..a2fae0a58 100644 --- a/src/tracks/quad.cpp +++ b/src/tracks/quad.cpp @@ -23,8 +23,6 @@ #include #include -#include "LinearMath/btTransform.h" - /** Constructor, takes 4 points. */ Quad::Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3) { @@ -102,24 +100,3 @@ bool Quad::pointInside(const Vec3& p, bool ignore_vertical) const p.sideOfLine2D(m_p[3], m_p[0]) >= 0.0; } } // pointInside - -// ---------------------------------------------------------------------------- -/** Transforms a quad by a given transform (i.e. translation+rotation). This - * function does not modify this quad, the results are stored in the quad - * specified as parameter. These functions are used for slipstreaming to - * determine the slipstream area from the original value (kart at 0,0,0 and - * no rotation) to the current value. - * \param t The transform to apply. - * \param result The quad which stores the result. - */ -void Quad::transform(const btTransform &t, Quad *result) const -{ - result->m_p[0] = t(m_p[0]); - result->m_p[1] = t(m_p[1]); - result->m_p[2] = t(m_p[2]); - result->m_p[3] = t(m_p[3]); - result->m_min_height = std::min ( std::min(result->m_p[0].getY(), - result->m_p[1].getY()), - std::min(result->m_p[2].getY(), - result->m_p[3].getY()) ); -} // transform diff --git a/src/tracks/quad.hpp b/src/tracks/quad.hpp index cd164fb7c..130bb2c9d 100644 --- a/src/tracks/quad.hpp +++ b/src/tracks/quad.hpp @@ -31,8 +31,6 @@ namespace irr } using namespace irr; -class btTransform; - /** * \ingroup tracks */ @@ -64,8 +62,6 @@ public: // ------------------------------------------------------------------------ void getVertices(video::S3DVertex *v, const video::SColor &color) const; // ------------------------------------------------------------------------ - void transform(const btTransform &t, Quad *result) const; - // ------------------------------------------------------------------------ /** Returns the i-th. point of a quad. */ const Vec3& operator[](int i) const { return m_p[i]; } // ------------------------------------------------------------------------ From 8c830bdabfbaa9b06854599c18dfd5552e181200 Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 15 Sep 2016 11:46:31 +0800 Subject: [PATCH 189/350] Inital work on one graph interface --- src/tracks/arena_graph.cpp | 343 ++++++++++++++++++ src/tracks/arena_graph.hpp | 84 +++++ src/tracks/arena_node.cpp | 45 +++ src/tracks/arena_node.hpp | 63 ++++ src/tracks/arena_node_3d.hpp | 49 +++ src/tracks/bounding_box_3d.hpp | 83 +++++ src/tracks/graph.cpp | 616 +++++++++++++++++++++++++++++++++ src/tracks/graph.hpp | 148 ++++++++ src/tracks/graph_node.hpp | 2 - src/tracks/node_3d.cpp | 30 +- src/tracks/node_3d.hpp | 18 +- src/tracks/quad.cpp | 4 +- src/tracks/quad.hpp | 44 ++- src/tracks/track.cpp | 11 +- 14 files changed, 1487 insertions(+), 53 deletions(-) create mode 100644 src/tracks/arena_graph.cpp create mode 100644 src/tracks/arena_graph.hpp create mode 100644 src/tracks/arena_node.cpp create mode 100644 src/tracks/arena_node.hpp create mode 100644 src/tracks/arena_node_3d.hpp create mode 100644 src/tracks/bounding_box_3d.hpp create mode 100644 src/tracks/graph.cpp create mode 100644 src/tracks/graph.hpp diff --git a/src/tracks/arena_graph.cpp b/src/tracks/arena_graph.cpp new file mode 100644 index 000000000..fab8bf114 --- /dev/null +++ b/src/tracks/arena_graph.cpp @@ -0,0 +1,343 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 SuperTuxKart Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "tracks/arena_graph.hpp" + +#include "io/file_manager.hpp" +#include "io/xml_node.hpp" +#include "race/race_manager.hpp" +#include "tracks/arena_node.hpp" +#include "utils/log.hpp" + +#include +#include + +// ----------------------------------------------------------------------------- +ArenaGraph::ArenaGraph(const std::string &navmesh, const XMLNode *node) + : Graph() +{ + loadNavmesh(navmesh); + + // Compute shortest distance from all nodes + for (unsigned int i = 0; i < getNumNodes(); i++) + computeDijkstra(i); + + sortNearbyNodes(); + if (node && race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER) + loadGoalNodes(node); + +} // ArenaGraph + +// ----------------------------------------------------------------------------- +ArenaNode* ArenaGraph::getNode(unsigned int i) const +{ + ArenaNode* n = dynamic_cast(m_all_nodes[i]); + assert(n!= NULL); + return n; +} // getNode + +// ----------------------------------------------------------------------------- +void ArenaGraph::differentNodeColor(int n, video::SColor* c) const +{ + std::set::iterator it; + it = m_red_node.find(n); + if (it != m_red_node.end()) + { + *c = video::SColor(255, 255, 0, 0); + return; + } + + it = m_blue_node.find(n); + if (it != m_blue_node.end()) + { + *c = video::SColor(255, 0, 0, 255); + return; + } + +} // differentNodeColor + +// ----------------------------------------------------------------------------- +void ArenaGraph::loadNavmesh(const std::string &navmesh) +{ + XMLNode *xml = file_manager->createXMLTree(navmesh); + if (xml->getName() != "navmesh") + { + Log::error("ArenaGraph", "NavMesh is invalid."); + delete xml; + return; + } + + std::vector all_vertices; + for (unsigned int i = 0; i < xml->getNumNodes(); i++) + { + const XMLNode *xml_node = xml->getNode(i); + if (xml_node->getName() == "vertices") + { + for (unsigned int i = 0; i < xml_node->getNumNodes(); i++) + { + const XMLNode *xml_node_node = xml_node->getNode(i); + if (!(xml_node_node->getName() == "vertex")) + { + Log::error("ArenaGraph", "Unsupported type '%s' found" + "in '%s' - ignored.", + xml_node_node->getName().c_str(), navmesh.c_str()); + continue; + } + + // Reading vertices + float x, y, z; + xml_node_node->get("x", &x); + xml_node_node->get("y", &y); + xml_node_node->get("z", &z); + Vec3 p(x, y, z); + m_bb_min.min(p); + m_bb_max.max(p); + all_vertices.push_back(p); + } + } + if (xml_node->getName() == "faces") + { + for (unsigned int i = 0; i < xml_node->getNumNodes(); i++) + { + const XMLNode *xml_node_node = xml_node->getNode(i); + if (xml_node_node->getName() != "face") + { + Log::error("NavMesh", "Unsupported type '%s' found in '%s'" + " - ignored.", + xml_node_node->getName().c_str(), navmesh.c_str()); + continue; + } + + // Reading quads + std::vector quad_index; + std::vector adjacent_quad_index; + xml_node_node->get("indices", &quad_index); + xml_node_node->get("adjacents", &adjacent_quad_index); + assert(quad_index.size() == 4); + + createQuad(all_vertices[quad_index[0]], + all_vertices[quad_index[1]], all_vertices[quad_index[2]], + all_vertices[quad_index[3]], m_all_nodes.size(), + false/*invisible*/, false/*ai_ignore*/, true/*is_arena*/); + + ArenaNode* cur_node = getNode(m_all_nodes.size() - 1); + cur_node->setAdjacentNodes(adjacent_quad_index); + } + } + } + delete xml; + + // Xml is loaded, now initialize the graph + const unsigned int n_nodes = getNumNodes(); + + m_distance_matrix = std::vector> + (n_nodes, std::vector(n_nodes, 9999.9f)); + for (unsigned int i = 0; i < n_nodes; i++) + { + ArenaNode* cur_node = getNode(i); + for (const int& adjacent : cur_node->getAdjacentNodes()) + { + Vec3 diff = getNode(adjacent)->getCenter() - cur_node->getCenter(); + float distance = diff.length(); + m_distance_matrix[i][adjacent] = distance; + } + m_distance_matrix[i][i] = 0.0f; + } + + // Allocate and initialise the previous node data structure: + m_parent_node = std::vector> + (n_nodes, std::vector(n_nodes, Graph::UNKNOWN_SECTOR)); + for (unsigned int i = 0; i < n_nodes; i++) + { + for (unsigned int j = 0; j < n_nodes; j++) + { + if (i == j || m_distance_matrix[i][j] >= 9899.9f) + m_parent_node[i][j] = -1; + else + m_parent_node[i][j] = i; + } // for j + } // for i + +} // loadNavmesh + +// ---------------------------------------------------------------------------- +/** 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_node[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_node[i][j] = k + */ +void ArenaGraph::computeDijkstra(int source) +{ + // Stores the distance (float) to 'source' from a specified node (int) + typedef std::pair IndDistPair; + + class Shortest + { + public: + bool operator()(const IndDistPair &p1, const IndDistPair &p2) + { + return p1.second > p2.second; + } + }; + + std::priority_queue, Shortest> queue; + IndDistPair begin(source, 0.0f); + queue.push(begin); + const unsigned int n = getNumNodes(); + std::vector visited; + visited.resize(n, false); + 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; + + for (const int& adjacent : getNode(cur_index)->getAdjacentNodes()) + { + // 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_node[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_node[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_node[i][j] = k + */ +void ArenaGraph::computeFloydWarshall() +{ + unsigned int n = getNumNodes(); + + // initialize m_parent_node with unknown_node so that if no path is found + // b/w i and j then m_parent_node[i][j] = -1 (UNKNOWN_SECTOR) + // AI must check this + m_parent_node = std::vector< std::vector > + (n, std::vector(n, Graph::UNKNOWN_SECTOR)); + 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_node[i][j]=-1; + else + m_parent_node[i][j] = i; + } + } + + for (unsigned int k = 0; k < n; k++) + { + for (unsigned int i = 0; i < n; i++) + { + for (unsigned int j = 0; j < n; j++) + { + if ((m_distance_matrix[i][k] + m_distance_matrix[k][j]) < + m_distance_matrix[i][j]) + { + m_distance_matrix[i][j] = + m_distance_matrix[i][k] + m_distance_matrix[k][j]; + m_parent_node[i][j] = m_parent_node[k][j]; + } + } + } + } + +} // computeFloydWarshall + +// ----------------------------------------------------------------------------- +void ArenaGraph::loadGoalNodes(const XMLNode *node) +{ + const XMLNode *check_node = node->getNode("checks"); + for (unsigned int i = 0; i < check_node->getNumNodes(); i++) + { + const XMLNode *goal = check_node->getNode(i); + if (goal->getName() =="goal") + { + Vec3 p1, p2; + bool first_goal = false; + goal->get("first_goal", &first_goal); + goal->get("p1", &p1); + goal->get("p2", &p2); + + int first = Graph::UNKNOWN_SECTOR; + findRoadSector(p1, &first, NULL, true); + int last = Graph::UNKNOWN_SECTOR; + findRoadSector(p2, &last, NULL, true); + + first_goal ? m_blue_node.insert(first) : m_red_node.insert(first); + first_goal ? m_blue_node.insert(last) : m_red_node.insert(last); + while (first != last) + { + // Find all the nodes which connect the two points of + // goal, notice: only work if it's a straight line + first = getNextShortestPath(first, last); + first_goal ? m_blue_node.insert(first) : + m_red_node.insert(first); + } + } + } +} // loadGoalNodes + +// ---------------------------------------------------------------------------- +void ArenaGraph::sortNearbyNodes() +{ + // Only save the nearby 8 nodes + const unsigned int try_count = 8; + for (unsigned int i = 0; i < getNumNodes(); i++) + { + // Get the distance to all nodes at i + ArenaNode* cur_node = getNode(i); + std::vector nearby_nodes; + std::vector dist = m_distance_matrix[i]; + + // Skip the same node + dist[i] = 999999.0f; + for (unsigned int j = 0; j < try_count; j++) + { + std::vector::iterator it = + std::min_element(dist.begin(), dist.end()); + const int pos = it - dist.begin(); + nearby_nodes.push_back(pos); + dist[pos] = 999999.0f; + } + cur_node->setNearbyNodes(nearby_nodes); + } + +} // sortNearbyNodes + +// ---------------------------------------------------------------------------- \ No newline at end of file diff --git a/src/tracks/arena_graph.hpp b/src/tracks/arena_graph.hpp new file mode 100644 index 000000000..29b7f226b --- /dev/null +++ b/src/tracks/arena_graph.hpp @@ -0,0 +1,84 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 SuperTuxKart Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_ARENA_GRAPH_HPP +#define HEADER_ARENA_GRAPH_HPP + +#include "tracks/graph.hpp" +#include "utils/cpp2011.hpp" + +#include + +class ArenaNode; +class XMLNode; + +/** + * \ingroup tracks + */ +class ArenaGraph : public Graph +{ +private: + /** The actual graph data structure, it is an adjacency matrix. */ + std::vector> m_distance_matrix; + + /** The matrix that is used to store computed shortest paths. */ + std::vector> m_parent_node; + + /** Used in soccer mode to colorize the goal lines in minimap. */ + std::set m_red_node; + + std::set m_blue_node; + + // ------------------------------------------------------------------------ + void loadGoalNodes(const XMLNode *node); + // ------------------------------------------------------------------------ + void loadNavmesh(const std::string &navmesh); + // ------------------------------------------------------------------------ + void sortNearbyNodes(); + // ------------------------------------------------------------------------ + void computeDijkstra(int n); + // ------------------------------------------------------------------------ + void computeFloydWarshall(); + // ------------------------------------------------------------------------ + virtual bool hasLapLine() const OVERRIDE { return false; } + // ------------------------------------------------------------------------ + virtual void differentNodeColor(int n, video::SColor* c) const OVERRIDE; + +public: + static ArenaGraph* get() { return dynamic_cast(m_graph); } + // ------------------------------------------------------------------------ + ArenaGraph(const std::string &navmesh, const XMLNode *node = NULL); + // ------------------------------------------------------------------------ + virtual ~ArenaGraph() {} + // ------------------------------------------------------------------------ + ArenaNode* getNode(unsigned int i) const; + // ------------------------------------------------------------------------ + /** Returns the next node on the shortest path from i to j. + * Note: m_parent_node[j][i] contains the parent of i on path from j to i, + * which is the next node on the path from i to j (undirected graph) + */ + int getNextShortestPath(int i, int j) const + { + if (i == Graph::UNKNOWN_SECTOR || j == Graph::UNKNOWN_SECTOR) + return Graph::UNKNOWN_SECTOR; + return m_parent_node[j][i]; + } + +}; // Graph + +#endif diff --git a/src/tracks/arena_node.cpp b/src/tracks/arena_node.cpp new file mode 100644 index 000000000..9141d5a3e --- /dev/null +++ b/src/tracks/arena_node.cpp @@ -0,0 +1,45 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "tracks/arena_node.hpp" + +// ---------------------------------------------------------------------------- +ArenaNode::ArenaNode(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, + const Vec3 &p3, const Vec3 &normal, + unsigned int node_index) + : Quad(p0, p1, p2, p3, normal, node_index) +{ + Vec3 lower_center = (p0 + p1) * 0.5f; + Vec3 upper_center = (p2 + p3) * 0.5f; + + m_line = core::line3df(lower_center.toIrrVector(), + upper_center.toIrrVector()); +} // ArenaNode + +// ---------------------------------------------------------------------------- +/** Returns the square of the distance between the given point and any point + * on the 'centre' line, i.e. the finite line from the middle point of the + * lower end of the node to the middle point of the upper end of the node + * which belongs to this node. + * \param xyz The point for which the distance to the line is computed. + */ +float ArenaNode::getDistance2FromPoint(const Vec3 &xyz) const +{ + core::vector3df closest = m_line.getClosestPoint(xyz.toIrrVector()); + return (closest-xyz.toIrrVector()).getLengthSQ(); +} // getDistance2FromPoint diff --git a/src/tracks/arena_node.hpp b/src/tracks/arena_node.hpp new file mode 100644 index 000000000..bcb867b40 --- /dev/null +++ b/src/tracks/arena_node.hpp @@ -0,0 +1,63 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_ARENA_NODE_HPP +#define HEADER_ARENA_NODE_HPP + +#include "tracks/quad.hpp" +#include "utils/cpp2011.hpp" + +#include + +/** + * \ingroup tracks + */ +class ArenaNode : public Quad +{ +private: + core::line3df m_line; + + std::vector m_adjacent_nodes; + + std::vector m_nearby_nodes; + +public: + ArenaNode(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, + const Vec3 &normal, unsigned int node_index); + // ------------------------------------------------------------------------ + virtual ~ArenaNode() {} + // ------------------------------------------------------------------------ + const std::vector& getAdjacentNodes() { return m_adjacent_nodes; } + // ------------------------------------------------------------------------ + const std::vector& getNearbyNodes() { return m_nearby_nodes; } + // ------------------------------------------------------------------------ + void setAdjacentNodes(const std::vector& nodes) + { + m_adjacent_nodes = nodes; + } + // ------------------------------------------------------------------------ + void setNearbyNodes(const std::vector& nodes) + { + m_nearby_nodes = nodes; + } + // ------------------------------------------------------------------------ + virtual float getDistance2FromPoint(const Vec3 &xyz) const OVERRIDE; + +}; + +#endif diff --git a/src/tracks/arena_node_3d.hpp b/src/tracks/arena_node_3d.hpp new file mode 100644 index 000000000..e4b19edeb --- /dev/null +++ b/src/tracks/arena_node_3d.hpp @@ -0,0 +1,49 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_ARENA_NODE_3D_HPP +#define HEADER_ARENA_NODE_3D_HPP + +#include "tracks/arena_node.hpp" +#include "tracks/bounding_box_3d.hpp" + +/** + * \ingroup tracks + */ +class ArenaNode3D : public ArenaNode, + public BoundingBox3D +{ +public: + ArenaNode3D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, + const Vec3 &normal, unsigned int node_index) + : ArenaNode(p0, p1, p2, p3, normal, node_index) + { + BoundingBox3D::init(p0, p1, p2, p3, normal); + } + // ------------------------------------------------------------------------ + virtual bool pointInside(const Vec3& p, + bool ignore_vertical = false) const OVERRIDE + { + return BoundingBox3D::pointInside(p); + } + // ------------------------------------------------------------------------ + virtual bool is3DQuad() const OVERRIDE { return true; } + +}; + +#endif diff --git a/src/tracks/bounding_box_3d.hpp b/src/tracks/bounding_box_3d.hpp new file mode 100644 index 000000000..4f4c5ebd6 --- /dev/null +++ b/src/tracks/bounding_box_3d.hpp @@ -0,0 +1,83 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_NODE_BOUNDING_BOX_3D_HPP +#define HEADER_NODE_BOUNDING_BOX_3D_HPP + +#include "utils/vec3.hpp" + +/** + * \ingroup tracks + */ +class BoundingBox3D +{ +private: + /** A 3D box using to check if a point lies inside a quad. + */ + Vec3 m_box_faces[6][4]; + +public: + // ------------------------------------------------------------------------ + bool pointInside(const Vec3& p) const + { + float side = p.sideofPlane(m_box_faces[0][0], m_box_faces[0][1], + m_box_faces[0][2]); + for (int i = 1; i < 6; i++) + { + if (side * p.sideofPlane(m_box_faces[i][0], m_box_faces[i][1], + m_box_faces[i][2]) < 0) + return false; + } + return true; + } + // ------------------------------------------------------------------------ + void init(const Vec3& p0, const Vec3& p1, const Vec3& p2, const Vec3& p3, + const Vec3& normal) + { + // Compute the node bounding box used by pointInside + Vec3 box_corners[8]; + float box_high = 5.0f; + float box_low = 1.0f; + box_corners[0] = p0 + box_high * normal; + box_corners[1] = p1 + box_high * normal; + box_corners[2] = p2 + box_high * normal; + box_corners[3] = p3 + box_high * normal; + box_corners[4] = p0 - box_low * normal; + box_corners[5] = p1 - box_low * normal; + box_corners[6] = p2 - box_low * normal; + box_corners[7] = p3 - box_low * normal; + + Vec3 box_faces[6][4] = + { + { box_corners[0], box_corners[1], box_corners[2], box_corners[3] }, + { box_corners[3], box_corners[2], box_corners[6], box_corners[7] }, + { box_corners[7], box_corners[6], box_corners[5], box_corners[4] }, + { box_corners[1], box_corners[0], box_corners[4], box_corners[5] }, + { box_corners[4], box_corners[0], box_corners[3], box_corners[7] }, + { box_corners[1], box_corners[5], box_corners[6], box_corners[2] } + }; + + for (unsigned int i = 0; i < 6 ; i++) + { + for (unsigned int j = 0; j < 4; j++) + m_box_faces[i][j] = box_faces[i][j]; + } + } +}; + +#endif \ No newline at end of file diff --git a/src/tracks/graph.cpp b/src/tracks/graph.cpp new file mode 100644 index 000000000..df24d7a4b --- /dev/null +++ b/src/tracks/graph.cpp @@ -0,0 +1,616 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 SuperTuxKart Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "tracks/graph.hpp" + +#include +#include +#include +#include + +#include "config/user_config.hpp" +#include "graphics/irr_driver.hpp" +#include "graphics/glwrap.hpp" +#include "graphics/shaders.hpp" +#include "graphics/rtts.hpp" +#include "modes/profile_world.hpp" +#include "tracks/arena_node_3d.hpp" +#include "tracks/node_2d.hpp" +#include "tracks/node_3d.hpp" +#include "utils/log.hpp" + +const int Graph::UNKNOWN_SECTOR = -1; +Graph *Graph::m_graph = NULL; +// ----------------------------------------------------------------------------- +Graph::Graph() +{ + m_scaling = 0; + m_node = NULL; + m_mesh = NULL; + m_mesh_buffer = NULL; + m_new_rtt = NULL; + m_bb_min = Vec3( 99999, 99999, 99999); + m_bb_max = Vec3(-99999, -99999, -99999); +} // Graph + +// ----------------------------------------------------------------------------- +Graph::~Graph() +{ + if (m_new_rtt != NULL) + { + delete m_new_rtt; + m_new_rtt = NULL; + } + + if (UserConfigParams::m_track_debug) + cleanupDebugMesh(); + + for (unsigned int i = 0; i < m_all_nodes.size(); i++) + { + delete m_all_nodes[i]; + } + m_all_nodes.clear(); +} // ~Graph + +// ----------------------------------------------------------------------------- +/** Creates the debug mesh to display the graph on top of the track + * model. */ +void Graph::createDebugMesh() +{ + if (getNumNodes() <= 0) return; // no debug output if not graph + + createMesh(/*show_invisible*/true, + /*enable_transparency*/true); + + video::S3DVertex *v = (video::S3DVertex*)m_mesh_buffer->getVertices(); + for (unsigned int i = 0; i < m_mesh_buffer->getVertexCount(); i++) + { + // Swap the alpha and back + v[i].Color.setAlpha((i%2) ? 64 : 255); + } + m_node = irr_driver->addMesh(m_mesh, "track-debug-mesh"); +#ifdef DEBUG + m_node->setName("track-debug-mesh"); +#endif + +} // createDebugMesh + +// ----------------------------------------------------------------------------- +/** Cleans up the debug mesh */ +void Graph::cleanupDebugMesh() +{ + if (m_node != NULL) + irr_driver->removeNode(m_node); + + m_node = NULL; + // No need to call irr_driber->removeMeshFromCache, since the mesh + // was manually made and so never added to the mesh cache. + m_mesh->drop(); + m_mesh = NULL; +} // cleanupDebugMesh + +// ----------------------------------------------------------------------------- +/** Creates the actual mesh that is used by createDebugMesh() or makeMiniMap() + */ +void Graph::createMesh(bool show_invisible, bool enable_transparency, + const video::SColor *track_color) +{ + // The debug track will not be lighted or culled. + video::SMaterial m; + m.BackfaceCulling = false; + m.Lighting = false; + if (enable_transparency) + m.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + m.setTexture(0, getUnicolorTexture(video::SColor(255, 255, 255, 255))); + m.setTexture(1, getUnicolorTexture(video::SColor(0, 0, 0, 0))); + m.setTexture(7, getUnicolorTexture(video::SColor(0, 0, 0, 0))); + m_mesh = irr_driver->createQuadMesh(&m); + m_mesh_buffer = m_mesh->getMeshBuffer(0); + assert(m_mesh_buffer->getVertexType()==video::EVT_STANDARD); + + unsigned int n = 0; + const unsigned int total_nodes = getNumNodes(); + + // Count the number of quads to display (some quads might be invisible) + for (unsigned int i = 0; i < total_nodes; i++) + { + if (show_invisible || !m_all_nodes[i]->isInvisible()) + n++; + } + + // Four vertices for each of the n-1 remaining quads + video::S3DVertex *new_v = new video::S3DVertex[4*n]; + // Each quad consists of 2 triangles with 3 elements, so + // we need 2*3 indices for each quad. + irr::u16 *ind = new irr::u16[6*n]; + video::SColor c(255, 255, 0, 0); + + if (track_color) + c = *track_color; + + // Now add all quads + int i = 0; + for (unsigned int count = 0; count < total_nodes; count++) + { + // Ignore invisible quads + if (!show_invisible && m_all_nodes[count]->isInvisible()) + continue; + + // Swap the colours from red to blue and back + if (!track_color) + { + c.setRed ((i%2) ? 255 : 0); + c.setBlue((i%2) ? 0 : 255); + } + + video::SColor this_color = c; + differentNodeColor(count, &this_color); + // Transfer the 4 points of the current quad to the list of vertices + m_all_nodes[count]->getVertices(new_v+4*i, this_color); + + // Set up the indices for the triangles + // (note, afaik with opengl we could use quads directly, but the code + // would not be portable to directx anymore). + ind[6*i ] = 4*i+2; // First triangle: vertex 0, 1, 2 + ind[6*i+1] = 4*i+1; + ind[6*i+2] = 4*i; + ind[6*i+3] = 4*i+3; // second triangle: vertex 0, 1, 3 + ind[6*i+4] = 4*i+2; + ind[6*i+5] = 4*i; + i++; + } + + m_mesh_buffer->append(new_v, n*4, ind, n*6); + + if (hasLapLine()) + { + video::S3DVertex lap_v[4]; + irr::u16 lap_ind[6]; + video::SColor lap_color(128, 255, 0, 0); + m_all_nodes[0]->getVertices(lap_v, lap_color); + + // Now scale the length (distance between vertix 0 and 3 + // and between 1 and 2) to be 'length': + // Length of the lap line about 3% of the 'height' + // of the track. + const float length = (m_bb_max.getZ()-m_bb_min.getZ())*0.03f; + + core::vector3df dl = lap_v[3].Pos-lap_v[0].Pos; + float ll2 = dl.getLengthSQ(); + if (ll2 < 0.001) + lap_v[3].Pos = lap_v[0].Pos+core::vector3df(0, 0, 1); + else + lap_v[3].Pos = lap_v[0].Pos+dl*length/sqrt(ll2); + + core::vector3df dr = lap_v[2].Pos-lap_v[1].Pos; + float lr2 = dr.getLengthSQ(); + if (lr2 < 0.001) + lap_v[2].Pos = lap_v[1].Pos+core::vector3df(0, 0, 1); + else + lap_v[2].Pos = lap_v[1].Pos+dr*length/sqrt(lr2); + lap_ind[0] = 2; + lap_ind[1] = 1; + lap_ind[2] = 0; + lap_ind[3] = 3; + lap_ind[4] = 2; + lap_ind[5] = 0; + // Set it a bit higher to avoid issued with z fighting, + // i.e. part of the lap line might not be visible. + for (unsigned int i = 0; i < 4; i++) + lap_v[i].Pos.Y += 0.1f; +#ifndef USE_TEXTURED_LINE + m_mesh_buffer->append(lap_v, 4, lap_ind, 6); +#else + lap_v[0].TCoords = core::vector2df(0,0); + lap_v[1].TCoords = core::vector2df(3,0); + lap_v[2].TCoords = core::vector2df(3,1); + lap_v[3].TCoords = core::vector2df(0,1); + m_mesh_buffer->append(lap_v, 4, lap_ind, 6); + video::SMaterial &m = m_mesh_buffer->getMaterial(); + video::ITexture *t = irr_driver->getTexture("chess.png"); + m.setTexture(0, t); +#endif + } + + // Instead of setting the bounding boxes, we could just disable culling, + // since the debug track should always be drawn. + //m_node->setAutomaticCulling(scene::EAC_OFF); + m_mesh_buffer->recalculateBoundingBox(); + m_mesh->setBoundingBox(m_mesh_buffer->getBoundingBox()); + + m_mesh_buffer->getMaterial().setTexture(0, irr_driver + ->getTexture("unlit.png")); + + delete[] ind; + delete[] new_v; +} // createMesh + +// ----------------------------------------------------------------------------- +/** Takes a snapshot of the graph so they can be used as minimap. + */ +void Graph::makeMiniMap(const core::dimension2du &dimension, + const std::string &name, + const video::SColor &fill_color, + video::ITexture** oldRttMinimap, + FrameBuffer** newRttMinimap) +{ + // Skip minimap when profiling + if (ProfileWorld::isNoGraphics()) return; + + const video::SColor oldClearColor = World::getWorld()->getClearColor(); + World::getWorld() + ->setClearbackBufferColor(video::SColor(0, 255, 255, 255)); + World::getWorld()->forceFogDisabled(true); + *oldRttMinimap = NULL; + *newRttMinimap = NULL; + + RTT* newRttProvider = NULL; + IrrDriver::RTTProvider* oldRttProvider = NULL; + if (CVS->isGLSL()) + { + m_new_rtt = newRttProvider = + new RTT(dimension.Width, dimension.Height); + } + else + { + oldRttProvider = new IrrDriver::RTTProvider(dimension, name, true); + } + + irr_driver->getSceneManager() + ->setAmbientLight(video::SColor(255, 255, 255, 255)); + + createMesh(/*show_invisible part of the track*/ false, + /*enable_transparency*/ false, + /*track_color*/ &fill_color); + + m_node = irr_driver->addMesh(m_mesh, "mini_map"); +#ifdef DEBUG + m_node->setName("minimap-mesh"); +#endif + + m_node->setAutomaticCulling(0); + m_node->setMaterialFlag(video::EMF_LIGHTING, false); + + // Add the camera: + // --------------- + scene::ICameraSceneNode *camera = irr_driver->addCameraSceneNode(); + Vec3 center = (m_bb_max+m_bb_min)*0.5f; + + float dx = m_bb_max.getX()-m_bb_min.getX(); + float dz = m_bb_max.getZ()-m_bb_min.getZ(); + + // Set the scaling correctly. Also the center point (which is used + // as the camera position) needs to be adjusted: the track must + // be aligned to the left/top of the texture which is used (otherwise + // mapPoint2MiniMap doesn't work), so adjust the camera position + // that the track is properly aligned (view from the side): + // c camera + // / \ . + // / \ <--- camera angle + // / \ . + // { [-] } <--- track flat (viewed from the side) + // If [-] is the shorter side of the track, then the camera will + // actually render the area in { } - which is the length of the + // longer side of the track. + // To align the [-] side to the left, the camera must be moved + // the distance betwwen '{' and '[' to the right. This distance + // is exacly (longer_side - shorter_side) / 2. + // So, adjust the center point by this amount: + if (dz > dx) + { + center.setX(center.getX() + (dz-dx)*0.5f); + m_scaling = dimension.Width / dz; + } + else + { + center.setZ(center.getZ() + (dx-dz)*0.5f); + m_scaling = dimension.Width / dx; + } + + float range = (dx>dz) ? dx : dz; + + core::matrix4 projection; + projection.buildProjectionMatrixOrthoLH + (range /* width */, range /* height */, -1, + m_bb_max.getY()-m_bb_min.getY()+1); + camera->setProjectionMatrix(projection, true); + + irr_driver->suppressSkyBox(); + irr_driver->clearLights(); + + // Adjust Y position by +1 for max, -1 for min - this helps in case that + // the maximum Y coordinate is negative (otherwise the minimap is mirrored) + // and avoids problems for tracks which have a flat (max Y=min Y) minimap. + camera->setPosition(core::vector3df(center.getX(), m_bb_min.getY() + 1.0f, + center.getZ())); + //camera->setPosition(core::vector3df(center.getX() - 5.0f, + // m_bb_min.getY() - 1 - 5.0f, center.getZ() - 15.0f)); + camera->setUpVector(core::vector3df(0, 0, 1)); + camera->setTarget(core::vector3df(center.getX(), m_bb_min.getY() - 1, + center.getZ())); + //camera->setAspectRatio(1.0f); + camera->updateAbsolutePosition(); + + video::ITexture* texture = NULL; + FrameBuffer* frame_buffer = NULL; + + if (CVS->isGLSL()) + { + frame_buffer = newRttProvider->render(camera, + GUIEngine::getLatestDt()); + } + else + { + texture = oldRttProvider->renderToTexture(); + delete oldRttProvider; + } + + cleanupDebugMesh(); + irr_driver->removeCameraSceneNode(camera); + + if (texture == NULL && frame_buffer == NULL) + { + Log::error("Graph", "[makeMiniMap] WARNING: RTT does not" + "appear to work, mini-map will not be available."); + } + + *oldRttMinimap = texture; + *newRttMinimap = frame_buffer; + World::getWorld()->setClearbackBufferColor(oldClearColor); + World::getWorld()->forceFogDisabled(false); + + irr_driver->getSceneManager()->clear(); + VAOManager::kill(); + irr_driver->clearGlowingNodes(); + irr_driver->clearLights(); + irr_driver->clearForcedBloom(); + irr_driver->clearBackgroundNodes(); +} // makeMiniMap + +// ----------------------------------------------------------------------------- +/** Returns the 2d coordinates of a point when drawn on the mini map + * texture. + * \param xyz Coordinates of the point to map. + * \param draw_at The coordinates in pixel on the mini map of the point, + * only the first two coordinates will be used. + */ +void Graph::mapPoint2MiniMap(const Vec3 &xyz,Vec3 *draw_at) const +{ + draw_at->setX((xyz.getX()-m_bb_min.getX())*m_scaling); + draw_at->setY((xyz.getZ()-m_bb_min.getZ())*m_scaling); + +} // mapPoint + +// ----------------------------------------------------------------------------- +void Graph::createQuad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, + const Vec3 &p3, unsigned int node_index, + bool invisible, bool ai_ignore, bool is_arena) +{ + // Find the normal of this quad by computing the normal of two triangles + // and taking their average. + core::triangle3df tri1(p0.toIrrVector(), p1.toIrrVector(), + p2.toIrrVector()); + core::triangle3df tri2(p0.toIrrVector(), p2.toIrrVector(), + p3.toIrrVector()); + Vec3 normal1 = tri1.getNormal(); + Vec3 normal2 = tri2.getNormal(); + Vec3 normal = -0.5f * (normal1 + normal2); + normal.normalize(); + + // Use the angle between the normal and an up vector to choose 3d/2d quad + const float angle = normal.angle(Vec3(0, 1, 0)); + + Quad* q = NULL; + if (angle > 0.5f) + { + Log::debug("Graph", "3d node created, normal: %f, %f, %f", + normal.x(), normal.y(), normal.z()); + if (is_arena) + { + q = new ArenaNode3D(p0, p1, p2, p3, normal, node_index); + } + else + { + q = new Node3D(p0, p1, p2, p3, normal, node_index, invisible, + ai_ignore); + } + } + + Log::debug("Graph", "2d node created, normal: %f, %f, %f", + normal.x(), normal.y(), normal.z()); + if (is_arena) + { + q = new ArenaNode(p0, p1, p2, p3, normal, node_index); + } + else + { + q = new Node2D(p0, p1, p2, p3, normal, node_index, invisible, + ai_ignore); + } + m_all_nodes.push_back(q); + +} // createQuad + +//----------------------------------------------------------------------------- +/** findRoadSector returns in which sector on the road the position + * xyz is. If xyz is not on top of the road, it sets UNKNOWN_SECTOR as sector. + * + * \param xyz Position for which the segment should be determined. + * \param sector Contains the previous sector (as a shortcut, since usually + * the sector is the same as the last one), and on return the result + * \param all_sectors If this is not NULL, it is a list of all sectors to + * test. This is used by the AI to make sure that it ends up on the + * selected way in case of a branch, and also to make sure that it + * doesn't skip e.g. a loop (see explanation below for details). + */ +void Graph::findRoadSector(const Vec3& xyz, int *sector, + std::vector *all_sectors, + bool ignore_vertical) const +{ + // Most likely the kart will still be on the sector it was before, + // so this simple case is tested first. + if (*sector!=UNKNOWN_SECTOR && + getQuad(*sector)->pointInside(xyz, ignore_vertical)) + { + return; + } // if still on same quad + + // Now we search through all quads, starting with + // the current one + int indx = *sector; + + // If a current sector is given, and max_lookahead is specify, only test + // the next max_lookahead quads instead of testing the whole graph. + // This is necessary for the AI: if the track contains a loop, e.g.: + // -A--+---B---+----F-------- + // E C + // +---D---+ + // and the track is supposed to be driven: ABCDEBF, the AI might find + // the quad on F, and then keep on going straight ahead instead of + // using the loop at all. + unsigned int max_count = (*sector!=UNKNOWN_SECTOR && all_sectors!=NULL) + ? (unsigned int)all_sectors->size() + : (unsigned int)m_all_nodes.size(); + *sector = UNKNOWN_SECTOR; + for(unsigned int i=0; ipointInside(xyz, ignore_vertical)) + { + *sector = indx; + } + } // for i *all_sectors, + bool ignore_vertical) const +{ + int count = (all_sectors!=NULL) ? (int)all_sectors->size() : getNumNodes(); + int current_sector = 0; + if(curr_sector != UNKNOWN_SECTOR && !all_sectors) + { + // We have to test all quads here: reason is that on track with + // shortcuts the n quads of the main drivelines is followed by + // the quads of the shortcuts. So after quad n-1 (the last one + // before the lap counting line) quad n will not be 0 (the first + // quad after the lap counting line), but one of the quads on a + // shortcut. If we only tested a limited number of quads to + // improve the performance the crossing of a lap might not be + // detected (because quad 0 is not tested, only quads on the + // shortcuts are tested). If this should become a performance + // bottleneck, we need to set up a graph of 'next' quads for each + // quad (similar to what the AI does), and only test the quads + // in this graph. + const int LIMIT = getNumNodes(); + count = LIMIT; + // Start 10 quads before the current quad, so the quads closest + // to the current position are tested first. + current_sector = curr_sector -10; + if(current_sector<0) current_sector += getNumNodes(); + } + + int min_sector = UNKNOWN_SECTOR; + float min_dist_2 = 999999.0f*999999.0f; + + // If a kart is falling and in between (or too far below) + // a driveline point it might not fulfill + // the height condition. So we run the test twice: first with height + // condition, then again without the height condition - just to make sure + // it always comes back with some kind of quad. + for(int phase=0; phase<2; phase++) + { + for(int j=0; jgetDistance2FromPoint(xyz); + if(dist_2getMinHeight(); + // While negative distances are unlikely, we allow some small + // negative numbers in case that the kart is partly in the + // track. Only do the height test in phase==0, in phase==1 + // accept any point, independent of height, or this node is 3d + // which already takes height into account + if(phase==1 || (dist < 5.0f && dist>-1.0f) || + q->is3DQuad() || ignore_vertical) + { + min_dist_2 = dist_2; + min_sector = next_sector; + } + } + current_sector = next_sector; + } // for j + // Leave in phase 0 if any sector was found. + if(min_sector!=UNKNOWN_SECTOR) + return min_sector; + } // phase + + if(min_sector==UNKNOWN_SECTOR) + { + Log::info("Graph", "unknown sector found."); + } + return min_sector; +} // findOutOfRoadSector + +//----------------------------------------------------------------------------- diff --git a/src/tracks/graph.hpp b/src/tracks/graph.hpp new file mode 100644 index 000000000..2925bf584 --- /dev/null +++ b/src/tracks/graph.hpp @@ -0,0 +1,148 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 SuperTuxKart Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_GRAPH_HPP +#define HEADER_GRAPH_HPP + +#include "utils/no_copy.hpp" +#include "utils/vec3.hpp" + +#include + +#include +#include + +namespace irr +{ + namespace scene { class ISceneNode; class IMesh; class IMeshBuffer; } + namespace video { class ITexture; struct S3DVertex; class SColor; } +} + +using namespace irr; + +class FrameBuffer; +class Quad; +class RTT; + +/** + * \ingroup tracks + */ +class Graph : public NoCopy +{ +protected: + static Graph* m_graph; + + std::vector m_all_nodes; + + /** The 2d bounding box, used for hashing. */ + Vec3 m_bb_min; + + Vec3 m_bb_max; + + // ------------------------------------------------------------------------ + /** Factory method to dynamic create 2d / 3d quad. */ + void createQuad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, + const Vec3 &p3, unsigned int node_index, + bool invisible, bool ai_ignore, bool is_arena); + +private: + RTT* m_new_rtt; + + /** The node of the graph mesh. */ + scene::ISceneNode *m_node; + + /** The mesh of the graph mesh. */ + scene::IMesh *m_mesh; + + /** The actual mesh buffer storing the graph. */ + scene::IMeshBuffer *m_mesh_buffer; + + /** Scaling for mini map. */ + float m_scaling; + + // ------------------------------------------------------------------------ + void createMesh(bool show_invisible=true, + bool enable_transparency=false, + const video::SColor *track_color=NULL); + // ------------------------------------------------------------------------ + void cleanupDebugMesh(); + // ------------------------------------------------------------------------ + virtual bool hasLapLine() const = 0; + // ------------------------------------------------------------------------ + virtual void differentNodeColor(int n, video::SColor* c) const = 0; + +public: + static const int UNKNOWN_SECTOR; + // ------------------------------------------------------------------------ + /** Returns the one instance of this object. It is possible that there + * is no instance created (e.g. battle mode without navmesh) so we don't + * assert that an instance exist. */ + static Graph* get() { return m_graph; } + // ------------------------------------------------------------------------ + /** Set the graph (either driveline or arena graph for now). */ + static void setGraph(Graph* graph) + { + assert(m_graph == NULL); + m_graph = graph; + } // create + // ------------------------------------------------------------------------ + /** Cleans up the graph. It is possible that this function is called even + * if no instance exists (e.g. in battle mode without navmesh). So it is + * not an error if there is no instance. */ + static void destroy() + { + if (m_graph) + { + delete m_graph; + m_graph = NULL; + } + } // destroy + // ------------------------------------------------------------------------ + Graph(); + // ------------------------------------------------------------------------ + virtual ~Graph(); + // ------------------------------------------------------------------------ + void createDebugMesh(); + // ------------------------------------------------------------------------ + void makeMiniMap(const core::dimension2du &where, const std::string &name, + const video::SColor &fill_color, + video::ITexture** oldRttMinimap, + FrameBuffer** newRttMinimap); + // ------------------------------------------------------------------------ + void mapPoint2MiniMap(const Vec3 &xyz, Vec3 *out) const; + // ------------------------------------------------------------------------ + Quad* getQuad(unsigned int i) const + { + assert(i < m_all_nodes.size()); + return m_all_nodes[i]; + } + // ------------------------------------------------------------------------ + unsigned int getNumNodes() const { return m_all_nodes.size(); } + // ------------------------------------------------------------------------ + void findRoadSector(const Vec3& XYZ, int *sector, + std::vector *all_sectors = NULL, + bool ignore_vertical = false) const; + // ------------------------------------------------------------------------ + int findOutOfRoadSector(const Vec3& xyz, + const int curr_sector = UNKNOWN_SECTOR, + std::vector *all_sectors = NULL, + bool ignore_vertical = false) const; + +}; // Graph + +#endif diff --git a/src/tracks/graph_node.hpp b/src/tracks/graph_node.hpp index 3263fd2c0..59e5ebac3 100644 --- a/src/tracks/graph_node.hpp +++ b/src/tracks/graph_node.hpp @@ -210,8 +210,6 @@ public: /** True if this node should be ignored by the AI. */ bool letAIIgnore() const { return m_ai_ignore; } // ------------------------------------------------------------------------ - virtual float getDistance2FromPoint(const Vec3 &xyz) const = 0; - // ------------------------------------------------------------------------ virtual void getDistances(const Vec3 &xyz, Vec3 *result) const = 0; }; // GraphNode diff --git a/src/tracks/node_3d.cpp b/src/tracks/node_3d.cpp index a83a51c8f..7cd7b3c4e 100644 --- a/src/tracks/node_3d.cpp +++ b/src/tracks/node_3d.cpp @@ -24,35 +24,7 @@ Node3D::Node3D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, bool ai_ignore) : GraphNode(p0, p1, p2, p3, normal, node_index, invisible, ai_ignore) { - // Compute the node bounding box used for pointInNode - Vec3 box_corners[8]; - float box_high = 5.0f; - float box_low = 1.0f; - box_corners[0] = m_p[0] + box_high * normal; - box_corners[1] = m_p[1] + box_high * normal; - box_corners[2] = m_p[2] + box_high * normal; - box_corners[3] = m_p[3] + box_high * normal; - box_corners[4] = m_p[0] - box_low * normal; - box_corners[5] = m_p[1] - box_low * normal; - box_corners[6] = m_p[2] - box_low * normal; - box_corners[7] = m_p[3] - box_low * normal; - - Vec3 box_faces[6][4] = - { - { box_corners[0], box_corners[1], box_corners[2], box_corners[3] }, - { box_corners[3], box_corners[2], box_corners[6], box_corners[7] }, - { box_corners[7], box_corners[6], box_corners[5], box_corners[4] }, - { box_corners[1], box_corners[0], box_corners[4], box_corners[5] }, - { box_corners[4], box_corners[0], box_corners[3], box_corners[7] }, - { box_corners[1], box_corners[5], box_corners[6], box_corners[2] } - }; - - for (unsigned int i = 0; i < 6 ; i++) - { - for (unsigned int j = 0; j < 4; j++) - m_box_faces[i][j] = box_faces[i][j]; - } - + BoundingBox3D::init(p0, p1, p2, p3, normal); m_line = core::line3df(m_lower_center.toIrrVector(), m_upper_center.toIrrVector()); } // Node3D diff --git a/src/tracks/node_3d.hpp b/src/tracks/node_3d.hpp index 8da8f271b..ec66ea9aa 100644 --- a/src/tracks/node_3d.hpp +++ b/src/tracks/node_3d.hpp @@ -19,19 +19,17 @@ #ifndef HEADER_NODE_3D_HPP #define HEADER_NODE_3D_HPP +#include "tracks/bounding_box_3d.hpp" #include "tracks/graph_node.hpp" #include "utils/cpp2011.hpp" /** * \ingroup tracks */ -class Node3D : public GraphNode +class Node3D : public GraphNode, + public BoundingBox3D { private: - /** For each node, construct a 3D box to check if a point lies inside it. - */ - Vec3 m_box_faces[6][4]; - /** Line between lower and upper center, saves computation in * getDistance() later. */ @@ -45,15 +43,7 @@ public: virtual bool pointInside(const Vec3& p, bool ignore_vertical = false) const OVERRIDE { - float side = p.sideofPlane(m_box_faces[0][0], m_box_faces[0][1], - m_box_faces[0][2]); - for (int i = 1; i < 6; i++) - { - if (side * p.sideofPlane(m_box_faces[i][0], m_box_faces[i][1], - m_box_faces[i][2]) < 0) - return false; - } - return true; + return BoundingBox3D::pointInside(p); } // ------------------------------------------------------------------------ virtual void getDistances(const Vec3 &xyz, Vec3 *result) const OVERRIDE; diff --git a/src/tracks/quad.cpp b/src/tracks/quad.cpp index a2fae0a58..c63fa2dc3 100644 --- a/src/tracks/quad.cpp +++ b/src/tracks/quad.cpp @@ -24,7 +24,9 @@ #include /** Constructor, takes 4 points. */ -Quad::Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3) +Quad::Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, + const Vec3 &normal, int index, bool invisible) + : m_index(index), m_normal(normal), m_invisible(invisible) { m_p[0]=p0; m_p[1]=p1; m_p[2]=p2; m_p[3]=p3; diff --git a/src/tracks/quad.hpp b/src/tracks/quad.hpp index 130bb2c9d..212396cc5 100644 --- a/src/tracks/quad.hpp +++ b/src/tracks/quad.hpp @@ -44,7 +44,16 @@ protected: * This saves some computations at runtime. */ Vec3 m_center; + /** Index of this quad, used only with graph. */ + int m_index; + + /** Normal of the quad */ + Vec3 m_normal; + private: + /** Set to true if this quad should not be shown in the minimap. */ + bool m_invisible; + /** The minimum height of the quad, used in case that several quads * are on top of each other when determining the sector a kart is on. */ float m_min_height; @@ -56,9 +65,11 @@ private: public: LEAK_CHECK() // ------------------------------------------------------------------------ - Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3); + Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, + const Vec3 & normal = Vec3(0, 1, 0), int index = -1, + bool invisible = false); // ------------------------------------------------------------------------ - virtual ~Quad() {} + virtual ~Quad() {} // ------------------------------------------------------------------------ void getVertices(video::S3DVertex *v, const video::SColor &color) const; // ------------------------------------------------------------------------ @@ -69,10 +80,37 @@ public: const Vec3& getCenter () const { return m_center; } // ------------------------------------------------------------------------ /** Returns the minimum height of a quad. */ - float getMinHeight() const { return m_min_height; } + float getMinHeight() const { return m_min_height; } // ------------------------------------------------------------------------ + /** Returns the index of this quad. */ + int getIndex() const + { + // You should not call this if it has default value (like slipstream) + assert(m_index != -1); + return m_index; + } + // ------------------------------------------------------------------------ + /** Returns true of this quad is invisible, i.e. not to be shown in + * the minimap. */ + bool isInvisible() const { return m_invisible; } + // ------------------------------------------------------------------------ + /** Returns the normal of this quad. */ + const Vec3& getNormal() const { return m_normal; } + // ------------------------------------------------------------------------ + /** Returns true if a point is inside this quad. */ virtual bool pointInside(const Vec3& p, bool ignore_vertical = false) const; + // ------------------------------------------------------------------------ + /** Returns true if this quad is 3D, which additional 3D testing is used in + * pointInside. */ + virtual bool is3DQuad() const { return false; } + // ------------------------------------------------------------------------ + virtual float getDistance2FromPoint(const Vec3 &xyz) const + { + // You should not call this in a bare quad + assert(false); + return 0.0f; + } }; // class Quad #endif diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index 854bcb6e6..2644ead4b 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -53,6 +53,7 @@ #include "physics/triangle_mesh.hpp" #include "race/race_manager.hpp" #include "scriptengine/script_engine.hpp" +#include "tracks/arena_graph.hpp" #include "tracks/bezier_curve.hpp" #include "tracks/battle_graph.hpp" #include "tracks/check_manager.hpp" @@ -668,12 +669,14 @@ void Track::startMusic() const } // startMusic //----------------------------------------------------------------------------- -/** Loads the polygon graph for battle, i.e. the definition of all polys, and the way - * they are connected to each other. Input file name is hardcoded for now +/** Loads the quad graph for arena, i.e. the definition of all quads, and the + * way they are connected to each other. Input file name is hardcoded for now */ void Track::loadBattleGraph(const XMLNode &node) { BattleGraph::create(m_root+"navmesh.xml", &node); + ArenaGraph* graph = new ArenaGraph(m_root+"navmesh.xml", &node); + Graph::setGraph(graph); if(BattleGraph::get()->getNumNodes()==0) { @@ -725,7 +728,7 @@ void Track::loadQuadGraph(unsigned int mode_id, const bool reverse) void Track::mapPoint2MiniMap(const Vec3 &xyz, Vec3 *draw_at) const { if ((m_is_arena || m_is_soccer) && m_has_navmesh) - BattleGraph::get()->mapPoint2MiniMap(xyz, draw_at); + Graph::get()->mapPoint2MiniMap(xyz, draw_at); else QuadGraph::get()->mapPoint2MiniMap(xyz, draw_at); draw_at->setX(draw_at->getX() * m_minimap_x_scale); @@ -1032,7 +1035,7 @@ void Track::loadMinimap() if ((m_is_arena || m_is_soccer) && m_has_navmesh) { - BattleGraph::get()->makeMiniMap(size, "minimap::" + m_ident, video::SColor(127, 255, 255, 255), + Graph::get()->makeMiniMap(size, "minimap::" + m_ident, video::SColor(127, 255, 255, 255), &m_old_rtt_mini_map, &m_new_rtt_mini_map); } else From 483dca3754e886c6cc3d7ec1b170ea4186379ece Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 15 Sep 2016 12:40:58 +0800 Subject: [PATCH 190/350] Make EndController to use length() --- src/karts/controller/end_controller.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/karts/controller/end_controller.cpp b/src/karts/controller/end_controller.cpp index d1188e0a1..7db4a0796 100644 --- a/src/karts/controller/end_controller.cpp +++ b/src/karts/controller/end_controller.cpp @@ -278,7 +278,7 @@ void EndController::findNonCrashingPoint(Vec3 *result) direction = QuadGraph::get()->getNode(target_sector)->getCenter() - m_kart->getXYZ(); - float len=direction.length_2d(); + float len=direction.length(); steps = int( len / m_kart_length ); if( steps < 3 ) steps = 3; From 05ad91c701cdd568866d271b2894dfe7e26c517b Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 15 Sep 2016 13:29:26 +0800 Subject: [PATCH 191/350] Port all function from battle graph to arena graph Todo: AI and worlds --- src/main.cpp | 6 +- src/tracks/arena_graph.cpp | 142 ++++++++++++++++++++++++++++++------- src/tracks/arena_graph.hpp | 21 +++++- src/tracks/arena_node.hpp | 5 ++ src/tracks/track.cpp | 10 ++- 5 files changed, 148 insertions(+), 36 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index a3100dc51..4146bb6ae 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -194,7 +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/arena_graph.hpp" #include "tracks/track.hpp" #include "tracks/track_manager.hpp" #include "utils/command_line.hpp" @@ -1884,8 +1884,8 @@ void runUnitTests() Log::info("UnitTest", "Kart characteristics"); CombinedCharacteristic::unitTesting(); - Log::info("UnitTest", "Battle Graph"); - BattleGraph::unitTesting(); + Log::info("UnitTest", "Arena Graph"); + ArenaGraph::unitTesting(); Log::info("UnitTest", "Fonts for translation"); font_manager->unitTesting(); diff --git a/src/tracks/arena_graph.cpp b/src/tracks/arena_graph.cpp index fab8bf114..7233a8c57 100644 --- a/src/tracks/arena_graph.cpp +++ b/src/tracks/arena_graph.cpp @@ -18,10 +18,13 @@ #include "tracks/arena_graph.hpp" +#include "config/user_config.hpp" #include "io/file_manager.hpp" #include "io/xml_node.hpp" #include "race/race_manager.hpp" #include "tracks/arena_node.hpp" +#include "tracks/track.hpp" +#include "tracks/track_manager.hpp" #include "utils/log.hpp" #include @@ -32,12 +35,12 @@ ArenaGraph::ArenaGraph(const std::string &navmesh, const XMLNode *node) : Graph() { loadNavmesh(navmesh); - + buildGraph(); // Compute shortest distance from all nodes for (unsigned int i = 0; i < getNumNodes(); i++) computeDijkstra(i); - sortNearbyNodes(); + setNearbyNodesOfAllNodes(); if (node && race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER) loadGoalNodes(node); @@ -69,6 +72,14 @@ void ArenaGraph::differentNodeColor(int n, video::SColor* c) const return; } + if (UserConfigParams::m_track_debug) + { + if (m_all_nodes[n]->is3DQuad()) + *c = video::SColor(255, 0, 255, 0); + else + *c = video::SColor(255, 255, 255, 0); + } + } // differentNodeColor // ----------------------------------------------------------------------------- @@ -142,7 +153,11 @@ void ArenaGraph::loadNavmesh(const std::string &navmesh) } delete xml; - // Xml is loaded, now initialize the graph +} // loadNavmesh + +// ---------------------------------------------------------------------------- +void ArenaGraph::buildGraph() +{ const unsigned int n_nodes = getNumNodes(); m_distance_matrix = std::vector> @@ -173,7 +188,7 @@ void ArenaGraph::loadNavmesh(const std::string &navmesh) } // for j } // for i -} // loadNavmesh +} // buildGraph // ---------------------------------------------------------------------------- /** Dijkstra shortest path computation. It computes the shortest distance from @@ -244,22 +259,6 @@ void ArenaGraph::computeFloydWarshall() { unsigned int n = getNumNodes(); - // initialize m_parent_node with unknown_node so that if no path is found - // b/w i and j then m_parent_node[i][j] = -1 (UNKNOWN_SECTOR) - // AI must check this - m_parent_node = std::vector< std::vector > - (n, std::vector(n, Graph::UNKNOWN_SECTOR)); - 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_node[i][j]=-1; - else - m_parent_node[i][j] = i; - } - } - for (unsigned int k = 0; k < n; k++) { for (unsigned int i = 0; i < n; i++) @@ -305,7 +304,7 @@ void ArenaGraph::loadGoalNodes(const XMLNode *node) { // Find all the nodes which connect the two points of // goal, notice: only work if it's a straight line - first = getNextShortestPath(first, last); + first = getNextNode(first, last); first_goal ? m_blue_node.insert(first) : m_red_node.insert(first); } @@ -314,7 +313,7 @@ void ArenaGraph::loadGoalNodes(const XMLNode *node) } // loadGoalNodes // ---------------------------------------------------------------------------- -void ArenaGraph::sortNearbyNodes() +void ArenaGraph::setNearbyNodesOfAllNodes() { // Only save the nearby 8 nodes const unsigned int try_count = 8; @@ -338,6 +337,101 @@ void ArenaGraph::sortNearbyNodes() cur_node->setNearbyNodes(nearby_nodes); } -} // sortNearbyNodes +} // setNearbyNodesOfAllNodes -// ---------------------------------------------------------------------------- \ No newline at end of file +// ---------------------------------------------------------------------------- +/** 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 ArenaGraph::getPathFromTo(int from, int to, + const std::vector< std::vector< int > > parent_node) +{ + std::vector path; + path.push_back(to); + while(from!=to) + { + to = parent_node[from][to]; + path.push_back(to); + } + return path; +} // getPathFromTo + +// ============================================================================ +/** 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 ArenaGraph::unitTesting() +{ + Track *track = track_manager->getTrack("cave"); + std::string navmesh_file_name=track->getTrackFile("navmesh.xml"); + + double s = StkTime::getRealTime(); + ArenaGraph* ag = new ArenaGraph(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 = ag->m_distance_matrix; + std::vector< std::vector< int > > parent_node = ag->m_parent_node; + ag->buildGraph(); + + // Now compute results with Floyd-Warshall + s = StkTime::getRealTime(); + ag->computeFloydWarshall(); + e = StkTime::getRealTime(); + Log::error("Time", "Floyd-Warshall %lf", e-s); + + int error_count = 0; + for(unsigned int i=0; im_distance_matrix.size(); i++) + { + for(unsigned int j=0; jm_distance_matrix[i].size(); j++) + { + if(ag->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], ag->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(ag->m_parent_poly[i][j] != parent_poly[i][j]) + { + error_count++; + std::vector dijkstra_path = getPathFromTo(i, j, parent_node); + std::vector floyd_path = getPathFromTo(i, j, ag->m_parent_node); + 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], ag->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 getPathFromTo(int from, int to, + const std::vector< std::vector< int > > parent_node); + // ------------------------------------------------------------------------ virtual bool hasLapLine() const OVERRIDE { return false; } // ------------------------------------------------------------------------ virtual void differentNodeColor(int n, video::SColor* c) const OVERRIDE; @@ -62,6 +67,8 @@ private: public: static ArenaGraph* get() { return dynamic_cast(m_graph); } // ------------------------------------------------------------------------ + static void unitTesting(); + // ------------------------------------------------------------------------ ArenaGraph(const std::string &navmesh, const XMLNode *node = NULL); // ------------------------------------------------------------------------ virtual ~ArenaGraph() {} @@ -72,13 +79,21 @@ public: * Note: m_parent_node[j][i] contains the parent of i on path from j to i, * which is the next node on the path from i to j (undirected graph) */ - int getNextShortestPath(int i, int j) const + int getNextNode(int i, int j) const { if (i == Graph::UNKNOWN_SECTOR || j == Graph::UNKNOWN_SECTOR) return Graph::UNKNOWN_SECTOR; return m_parent_node[j][i]; } + // ------------------------------------------------------------------------ + /** Returns the distance between any two nodes */ + float getDistance(int from, int to) const + { + if (from == Graph::UNKNOWN_SECTOR || to == Graph::UNKNOWN_SECTOR) + return 99999.0f; + return m_distance_matrix[from][to]; + } -}; // Graph +}; // ArenaGraph #endif diff --git a/src/tracks/arena_node.hpp b/src/tracks/arena_node.hpp index bcb867b40..666392541 100644 --- a/src/tracks/arena_node.hpp +++ b/src/tracks/arena_node.hpp @@ -56,6 +56,11 @@ public: m_nearby_nodes = nodes; } // ------------------------------------------------------------------------ + /** Returns true if the quad lies near the edge, which means it doesn't + * have 4 adjacent quads. + */ + bool isNearEdge() const { return m_adjacent_nodes.size() != 4; } + // ------------------------------------------------------------------------ virtual float getDistance2FromPoint(const Vec3 &xyz) const OVERRIDE; }; diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index 2644ead4b..d0899d386 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -1882,17 +1882,15 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id) if ((m_is_arena || m_is_soccer) && !m_is_cutscene && m_has_navmesh && !arena_random_item_created) BattleGraph::get()->findItemsOnGraphNodes(); - if (UserConfigParams::m_track_debug && + /*if (UserConfigParams::m_track_debug && race_manager->getMinorMode()!=RaceManager::MINOR_MODE_3_STRIKES && !m_is_cutscene) { QuadGraph::get()->createDebugMesh(); - } + }*/ - if (UserConfigParams::m_track_debug && m_has_navmesh && - race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES && - !m_is_cutscene) - BattleGraph::get()->createDebugMesh(); + if (UserConfigParams::m_track_debug && Graph::get() && !m_is_cutscene) + Graph::get()->createDebugMesh(); // Only print warning if not in battle mode, since battle tracks don't have // any quads or check lines. From 1491236e8425501c529893c303575d1780b563ee Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 15 Sep 2016 15:47:17 +0800 Subject: [PATCH 192/350] Allow TrackSector to use with new graph class --- src/karts/controller/arena_ai.hpp | 1 - src/karts/controller/soccer_ai.cpp | 1 - src/modes/soccer_world.cpp | 58 ++++++++++++++++++++++-------- src/modes/soccer_world.hpp | 13 +++---- src/modes/three_strikes_battle.cpp | 33 ++++++++++++----- src/modes/three_strikes_battle.hpp | 6 ++-- src/tracks/arena_graph.cpp | 10 +++--- src/tracks/arena_node.hpp | 2 +- src/tracks/track_sector.cpp | 28 +++++++++++---- src/tracks/track_sector.hpp | 2 +- 10 files changed, 104 insertions(+), 50 deletions(-) diff --git a/src/karts/controller/arena_ai.hpp b/src/karts/controller/arena_ai.hpp index a30c87bff..40122e5b6 100644 --- a/src/karts/controller/arena_ai.hpp +++ b/src/karts/controller/arena_ai.hpp @@ -21,7 +21,6 @@ #include "karts/controller/ai_base_controller.hpp" #include "race/race_manager.hpp" -#include "utils/random_generator.hpp" #undef AI_DEBUG #ifdef AI_DEBUG diff --git a/src/karts/controller/soccer_ai.cpp b/src/karts/controller/soccer_ai.cpp index 85ff79222..146fdffd0 100644 --- a/src/karts/controller/soccer_ai.cpp +++ b/src/karts/controller/soccer_ai.cpp @@ -24,7 +24,6 @@ #include "karts/controller/kart_control.hpp" #include "karts/kart_properties.hpp" #include "modes/soccer_world.hpp" -#include "tracks/battle_graph.hpp" #ifdef AI_DEBUG #include "irrlicht.h" diff --git a/src/modes/soccer_world.cpp b/src/modes/soccer_world.cpp index a56c32728..eba9e1479 100644 --- a/src/modes/soccer_world.cpp +++ b/src/modes/soccer_world.cpp @@ -34,9 +34,9 @@ #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 "tracks/track_sector.hpp" #include "utils/constants.hpp" #include @@ -61,6 +61,7 @@ SoccerWorld::SoccerWorld() : WorldWithRank() m_use_highscores = false; m_red_ai = 0; m_blue_ai = 0; + m_ball_track_sector = NULL; } // SoccerWorld //----------------------------------------------------------------------------- @@ -69,6 +70,15 @@ SoccerWorld::SoccerWorld() : WorldWithRank() SoccerWorld::~SoccerWorld() { m_goal_sound->deleteSFX(); + + delete m_ball_track_sector; + m_ball_track_sector = NULL; + for (unsigned int i = 0; i < m_kart_track_sector.size(); i++) + { + delete m_kart_track_sector[i]; + } + m_kart_track_sector.clear(); + } // ~SoccerWorld //----------------------------------------------------------------------------- @@ -88,6 +98,14 @@ void SoccerWorld::init() m_goal_target = race_manager->getMaxGoal(); m_goal_sound = SFXManager::get()->createSoundSource("goal_scored"); + if (m_track->hasNavMesh()) + { + // Init track sector if navmesh is found + m_ball_track_sector = new TrackSector(); + for (unsigned int i = 0; i < m_karts.size(); i++) + m_kart_track_sector.push_back(new TrackSector()); + } + TrackObjectManager* tom = getTrack()->getTrackObjectManager(); assert(tom); PtrVector& objects = tom->getObjects(); @@ -141,10 +159,17 @@ void SoccerWorld::reset() m_goal_sound->stop(); } + if (m_track->hasNavMesh()) + { + m_ball_track_sector->reset(); + for (unsigned int i = 0; i < m_karts.size(); i++) + m_kart_track_sector[i]->reset(); + } + initKartList(); - resetAllPosition(); m_ball->reset(); m_bgd.reset(); + // Make the player kart in profiling mode up // ie make this kart less likely to affect gaming result if (UserConfigParams::m_arena_ai_stats) @@ -439,9 +464,7 @@ void SoccerWorld::updateKartNodes() for (unsigned int i = 0; i < n; i++) { if (m_karts[i]->isEliminated()) continue; - - m_kart_on_node[i] = BattleGraph::get()->pointToNode(m_kart_on_node[i], - m_karts[i]->getXYZ(), false/*ignore_vertical*/); + m_kart_track_sector[i]->update(m_karts[i]->getXYZ()); } } // updateKartNodes @@ -461,11 +484,9 @@ void SoccerWorld::updateBallPosition(float dt) if (m_track->hasNavMesh()) { - m_ball_on_node = BattleGraph::get()->pointToNode(m_ball_on_node, - getBallPosition(), true/*ignore_vertical*/); - - if (m_ball_on_node == BattleGraph::UNKNOWN_POLY && - getPhase() == RACE_PHASE) + m_ball_track_sector + ->update(getBallPosition(), true/*ignore_vertical*/); + if (!m_ball_track_sector->isOnRoad() && getPhase() == RACE_PHASE) { m_ball_invalid_timer += dt; // Reset the ball and karts if out of navmesh after 2 seconds @@ -486,12 +507,19 @@ void SoccerWorld::updateBallPosition(float dt) } // updateBallPosition //----------------------------------------------------------------------------- -void SoccerWorld::resetAllPosition() +int SoccerWorld::getKartNode(unsigned int kart_id) const { - m_kart_on_node.clear(); - m_kart_on_node.resize(m_karts.size(), BattleGraph::UNKNOWN_POLY); - m_ball_on_node = BattleGraph::UNKNOWN_POLY; -} // resetAllPosition + assert(kart_id < m_kart_track_sector.size()); + return m_kart_track_sector[kart_id]->getCurrentGraphNode(); +} // getKartNode + +//----------------------------------------------------------------------------- +int SoccerWorld::getBallNode() const +{ + assert(m_ball_track_sector != NULL); + return m_ball_track_sector->getCurrentGraphNode(); +} // getBallNode + //----------------------------------------------------------------------------- SoccerTeam SoccerWorld::getKartTeam(unsigned int kart_id) const { diff --git a/src/modes/soccer_world.hpp b/src/modes/soccer_world.hpp index 5875d6c4f..97d57b2e5 100644 --- a/src/modes/soccer_world.hpp +++ b/src/modes/soccer_world.hpp @@ -31,6 +31,7 @@ class AbstractKart; class Controller; class TrackObject; +class TrackSector; /** An implementation of World, to provide the soccer game mode * Notice: In soccer world, true goal means blue, false means red. @@ -279,8 +280,8 @@ private: std::map m_kart_position_map; /** Data generated from navmesh */ - std::vector m_kart_on_node; - int m_ball_on_node; + std::vector m_kart_track_sector; + TrackSector* m_ball_track_sector; int m_red_ai; int m_blue_ai; @@ -293,8 +294,6 @@ private: void updateKartNodes(); /** Function to update the location the ball on the polygon map */ void updateBallPosition(float dt); - /** Clean up */ - void resetAllPosition(); /** Function to update data for AI usage. */ void updateAIData(); /** Get number of teammates in a team, used by starting position assign. */ @@ -357,11 +356,9 @@ public: m_blue_score_times : m_red_score_times); } // ------------------------------------------------------------------------ - int getKartNode(unsigned int kart_id) const - { return m_kart_on_node[kart_id]; } + int getKartNode(unsigned int kart_id) const; // ------------------------------------------------------------------------ - int getBallNode() const - { return m_ball_on_node; } + int getBallNode() const; // ------------------------------------------------------------------------ const Vec3& getBallPosition() const { return (Vec3&)m_ball_body->getCenterOfMassTransform().getOrigin(); } diff --git a/src/modes/three_strikes_battle.cpp b/src/modes/three_strikes_battle.cpp index 83282c045..535671d1c 100644 --- a/src/modes/three_strikes_battle.cpp +++ b/src/modes/three_strikes_battle.cpp @@ -28,9 +28,9 @@ #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 "tracks/track_sector.hpp" #include "utils/constants.hpp" #include @@ -65,6 +65,12 @@ void ThreeStrikesBattle::init() WorldWithRank::init(); m_display_rank = false; m_kart_info.resize(m_karts.size()); + if (m_track->hasNavMesh()) + { + // Init track sector if navmesh is found + for (unsigned int i = 0; i < m_karts.size(); i++) + m_kart_track_sector.push_back(new TrackSector()); + } } // ThreeStrikesBattle //----------------------------------------------------------------------------- @@ -80,6 +86,12 @@ ThreeStrikesBattle::~ThreeStrikesBattle() // freed once all refernces to it (which will happen once all // karts are being freed, which would have a pointer to this mesh) irr_driver->removeMeshFromCache(m_tire); + + for (unsigned int i = 0; i < m_kart_track_sector.size(); i++) + { + delete m_kart_track_sector[i]; + } + m_kart_track_sector.clear(); } // ~ThreeStrikesBattle //----------------------------------------------------------------------------- @@ -93,8 +105,7 @@ void ThreeStrikesBattle::reset() for(unsigned int n=0; nsetPosition(-1); @@ -134,6 +145,12 @@ void ThreeStrikesBattle::reset() m_track->getTrackObjectManager()->removeObject(obj); } m_tires.clearWithoutDeleting(); + + if (m_track->hasNavMesh()) + { + for (unsigned int i = 0; i < kart_amount; i++) + m_kart_track_sector[i]->reset(); + } } // reset //----------------------------------------------------------------------------- @@ -470,19 +487,17 @@ void ThreeStrikesBattle::updateKartNodes() for (unsigned int i = 0; i < n; i++) { if (m_karts[i]->isEliminated()) continue; - - m_kart_info[i].m_on_node = BattleGraph::get() - ->pointToNode(m_kart_info[i].m_on_node, - m_karts[i]->getXYZ(), false/*ignore_vertical*/); + m_kart_track_sector[i]->update(m_karts[i]->getXYZ()); } -} +} // updateKartNodes //----------------------------------------------------------------------------- /** Get the which node the kart located in navigation mesh. */ int ThreeStrikesBattle::getKartNode(unsigned int kart_id) const { - return m_kart_info[kart_id].m_on_node; + assert(kart_id < m_kart_track_sector.size()); + return m_kart_track_sector[kart_id]->getCurrentGraphNode(); } // getKartNode //----------------------------------------------------------------------------- diff --git a/src/modes/three_strikes_battle.hpp b/src/modes/three_strikes_battle.hpp index 2d01a646b..07f90ad27 100644 --- a/src/modes/three_strikes_battle.hpp +++ b/src/modes/three_strikes_battle.hpp @@ -29,6 +29,7 @@ #include class PhysicalObject; +class TrackSector; /** * \brief An implementation of World, to provide the 3 strikes battle game mode @@ -40,7 +41,6 @@ private: struct BattleInfo { int m_lives; - int m_on_node; }; /** This vector contains an 'BattleInfo' struct for every kart in the race. @@ -71,7 +71,9 @@ private: PtrVector m_tires; - /** Function to update the locations of all karts on the polygon map */ + std::vector m_kart_track_sector; + + /** Function to update the locations of all karts on the navigation map */ void updateKartNodes(); /** Profiling usage */ diff --git a/src/tracks/arena_graph.cpp b/src/tracks/arena_graph.cpp index 7233a8c57..9c1f65759 100644 --- a/src/tracks/arena_graph.cpp +++ b/src/tracks/arena_graph.cpp @@ -357,7 +357,7 @@ std::vector ArenaGraph::getPathFromTo(int from, int to, } // getPathFromTo // ============================================================================ -/** Unit testing for battle graph distance and parent node computation. +/** Unit testing for arena 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 @@ -391,7 +391,7 @@ void ArenaGraph::unitTesting() { if(ag->m_distance_matrix[i][j] - distance_matrix[i][j] > 0.001f) { - Log::error("BattleGraph", + Log::error("ArenaGraph", "Incorrect distance %d, %d: Dijkstra: %f F.W.: %f", i, j, distance_matrix[i][j], ag->m_distance_matrix[i][j]); error_count++; @@ -412,17 +412,17 @@ void ArenaGraph::unitTesting() std::vector floyd_path = getPathFromTo(i, j, ag->m_parent_node); if(dijkstra_path.size()!=floyd_path.size()) { - Log::error("BattleGraph", + Log::error("ArenaGraph", "Incorrect path length %d, %d: Dijkstra: %d F.W.: %d", i, j, parent_poly[i][j], ag->m_parent_poly[i][j]); continue; } - Log::error("BattleGraph", "Path problems from %d to %d:", + Log::error("ArenaGraph", "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", + Log::error("ArenaGraph", "%d/%d dijkstra: %d floyd %d", k, dijkstra_path.size(), dijkstra_path[k], floyd_path[k]); } // for k& getAdjacentNodes() { return m_adjacent_nodes; } // ------------------------------------------------------------------------ - const std::vector& getNearbyNodes() { return m_nearby_nodes; } + std::vector* getNearbyNodes() { return &m_nearby_nodes; } // ------------------------------------------------------------------------ void setAdjacentNodes(const std::vector& nodes) { diff --git a/src/tracks/track_sector.cpp b/src/tracks/track_sector.cpp index 6273b4999..8f1e8a28f 100644 --- a/src/tracks/track_sector.cpp +++ b/src/tracks/track_sector.cpp @@ -21,6 +21,8 @@ #include "tracks/check_manager.hpp" #include "tracks/check_structure.hpp" #include "tracks/track_sector.hpp" +#include "tracks/arena_graph.hpp" +#include "tracks/arena_node.hpp" #include "tracks/quad_graph.hpp" #include "tracks/graph_node.hpp" @@ -46,23 +48,35 @@ void TrackSector::reset() * the specified point. * \param xyz The new coordinates to search the graph node for. */ -void TrackSector::update(const Vec3 &xyz) +void TrackSector::update(const Vec3 &xyz, bool ignore_vertical) { int prev_sector = m_current_graph_node; - QuadGraph::get()->findRoadSector(xyz, &m_current_graph_node); - m_on_road = m_current_graph_node != QuadGraph::UNKNOWN_SECTOR; + const ArenaGraph* ag = ArenaGraph::get(); + std::vector* test_nodes = NULL; + if (ag) + { + // For ArenaGraph, only test nodes around current node + if (prev_sector != Graph::UNKNOWN_SECTOR) + test_nodes = ag->getNode(prev_sector)->getNearbyNodes(); + } + + Graph::get()->findRoadSector(xyz, &m_current_graph_node, test_nodes, + ignore_vertical); + m_on_road = m_current_graph_node != Graph::UNKNOWN_SECTOR; // If m_track_sector == UNKNOWN_SECTOR, then the kart is not on top of // the road, so we have to use search for the closest graph node. - if(m_current_graph_node == QuadGraph::UNKNOWN_SECTOR) + if(m_current_graph_node == Graph::UNKNOWN_SECTOR) { - m_current_graph_node = - QuadGraph::get()->findOutOfRoadSector(xyz, - prev_sector); + m_current_graph_node = Graph::get()->findOutOfRoadSector(xyz, + prev_sector, test_nodes, ignore_vertical); + // ArenaGraph (battle and soccer mode) doesn't need the code below + if (ag) return; } else { + if (ag) return; // keep the current quad as the latest valid one IF the player has one // of the required checklines const GraphNode* gn = QuadGraph::get()->getNode(m_current_graph_node); diff --git a/src/tracks/track_sector.hpp b/src/tracks/track_sector.hpp index 138db8e6f..f10716dd0 100644 --- a/src/tracks/track_sector.hpp +++ b/src/tracks/track_sector.hpp @@ -57,7 +57,7 @@ public: TrackSector(); void reset(); void rescue(); - void update(const Vec3 &xyz); + void update(const Vec3 &xyz, bool ignore_vertical = false); float getRelativeDistanceToCenter() const; // ------------------------------------------------------------------------ /** Returns how far the the object is from the start line. */ From f2bf4eb5490cafcc53f1e65b04cc384a94195fa0 Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Thu, 15 Sep 2016 20:00:00 -0400 Subject: [PATCH 193/350] Add new soccer ball icons --- data/gui/License.txt | 2 ++ data/gui/soccer_ball_blue.png | Bin 9029 -> 18005 bytes data/gui/soccer_ball_normal.png | Bin 11275 -> 14722 bytes data/gui/soccer_ball_red.png | Bin 8845 -> 16789 bytes 4 files changed, 2 insertions(+) diff --git a/data/gui/License.txt b/data/gui/License.txt index c6f1edb3c..0b57b1285 100644 --- a/data/gui/License.txt +++ b/data/gui/License.txt @@ -12,6 +12,8 @@ mode_tt, released under Creative-Commons BY-SA 3, incorporatings artwork from ye mode_easter, mode_ftl, mode_soccer, mode_normal, race_giveup: by Totoplus62, released under CC-BY-SA 3.0 +soccer balls released under CC-BY-SA 3.0 by Néd J. Édoire + tutorial.png by Totoplus62, released under CC-BY-SA 3.0 Logo and Logo_slim : under CC-BY-SA 3.0+ by 'rastapax' diff --git a/data/gui/soccer_ball_blue.png b/data/gui/soccer_ball_blue.png index 8c440784bcbef63bd1a9715af2a69329de292f33..9ce6823c2e359bae8ccb114c42ba22b95b113758 100644 GIT binary patch literal 18005 zcmXtA1yCE`*A1THTCBLcy9Rf6mtw_=y9Frj?p`SFuEpJ5ixjuw{^j?d`7*QF*`3V3 z-M3fHJ@>pss3=LJAQB(~000zO83{GWKJdQ{2oL%07d9P->|kBQWHo@0k1z0RIOO=# zcNtw5$nTf`+n~f~vpgXu@m(c#T-6;dT|G>lEdU-K9?Uilwl3zT-z}IOovpGi1PK5D zQh=-kNW(LGv&$=+NZaf1O{lc|V#%jPuY=Jm!Tt*VH+vr*CMG6~EKt`Hm=W@o8YXFi z5;Q8{$o|!mJ;~zVDElbyX_mcb5VEN(wC4yV23~ZZQg1!mkY@swej-&;BPvxxP=%&1>!(qnyNJsh-iz%|lAE8dXWSD*gLDOV1d9wgh@- zJ@Dqh*{fQwYHDhFnx(VW_~keD-5qM+`p27)ezKYeZHZAOwtkA*I`+3LH9K`Vdlfl4 z?A92lh!mNBC1@YGe)pp<->3L&;yNy#13t;6VyweLX!<8PdX1RayYMxjRJy6y#w1U7V?K6EK^J}w|p z{Na_2(7gw&W)J3csCB-e&3U2-!Q>DWoERAaE`D=s`i2KUbmh$&IQL2Yj4N_g7w2~t zFutrufjhR0{ollWNbU4M@l+o^lLy@q4B)cS7JRSzsVg!*JKY&(?HL)v;?D%q03$0P zmT=}gGu|~!*E?V6uDM97#Bd;M$}1=+unG$+S&CLeMN*~=rldR@w`9h22;UZ<3EijI z_eHT7V*yq=96uNWx1o)k*dFc{pRgsm@ZzycaP(|^(0-^JzY45PO8#`;2H|O{fMnXZ z#eNxpY+|$sN;vfx)RIgRVUvCq%Z8QAhv{Z|MLr*w#+U3NrLfFX_;hMF?n^L#`?Cq%<8tt0F>jB_*E*o{~+o4U={Gj#nS5Y1coxX z?0(aRxr8x#_swk>>rH}g%2YwwC! z;u;sVa!#%GooLhOUbwD49|<kH|E#D6=kEjPt<{8Zk=v5wb4Pk1b}`WlHqDs+Vcy>3+oh%R zLj~TdKa3unhBS{#IVxH#E^;{0igx9@)V8}MI1(tkVe0gklq2q)Xu0!#yD%72?o@L* z9f6^qBACEe?6FQiG8+T)yxR+E%M zP-rgybW5E4Uu?i@mE&8-;o{CIqcP5ZBlVP1y|K!5p+<%TypzT^2IH9<@={IszQ0jU zQZ;v;`E0UD+M98x{#q-#Ts{w!Flx4IGvGvfSFjHP%97%|B)8r z7K^!``H}x?p8mLR;mUJ-snNXE{`Uvq^L{Qp#5F2JYh)D{Zcg4Y8Gx%}n=?pBq>u?M zwpQ)(=;$BXIB#;3d)cQNIyQn0(xKyQ?OE@i{`mJ1`Eu_)+AVLfU5TC1m~Y&wYb#fR zsl9PO&HJ$6m(B^fh)2(dbo2J%a*s;ci}CwB$+lCSy+7Ll17cuW=UZ@-8n&N7fkTV$ zUYjo$@CX4K13fq$o9&aC8D$L99|6U9Y-~=;yn`L%H^!7vh85Rw56gP~${6(IFhtmp z=mk{dTqLCA2Mvhmy(#as<?X z<9bfm6H6+52LZJn+y0urP`YqS?w^+okYMkl`zY-dN@QCqRh`(9Q zeAn+7rv+u#Y~qKex=-aFjZA2h&5G1~|25~mKd!SN7K)M#C3BwLknV~%%L8W?R+l}tTso)p?ln0 zzd!%@HLh5C^*hbLx9#ry@~sAV39NuBo5}Fx%kxpqqh_OafC5{mG*xTOI84m*-eKR( zE6?er7pUh^8RbeQm_?5o)XrVO57qcfejaXd49sWGj(C6dbWi2xGy@&u2qNd3+8Nxo)qkeb8B30dWO-z-1He{$K#=zw@}kLymgz_w8S zF-yCFJIl^e%g0kzO8P?(Y$0^vNh?*GGp0rmC7aC#^a0p?YPdSjJMziuqSXIHmS5hg zOsnF*WWdJOx4%!h_$`IB7Gux5YYIzQuy3`&YW4br#QZigZNCt!EY?vP!PGeKL##Ug z!o`j$4$9fQF3-(Tf^{f=h0RT!|NufIvobArfx(<)?m(Ggg7}OGtPmdK{FZaebzQ^vml)dL7ceBQ@{!nnc z2043qPgp9MlRn#ERA`CHGJ{;N?6y+j!G96Mv}y9uv9av@{A+^;{)9hFaa+(5VGpNr z_GS+=?E)6p*%Q7<;K)(T2lEkIxWMl;)31M1T=ox;tGPb|R;==|4f=rNQg*Kx=MW%Lvq_f9s#;%P1khaqRv z?B9=8=lo1M?RVm#!{?KJqr5#{(D8xw-?2@$clNJevfK_EBQrDcw!FGc=LhaR=Rz$` zds68l5(atw0n!95Y&PxXf7oLmP~(k;aqVpY2Lw;xkdtL)E`L zy%^ItFk3!^n2ta&Rf*cCDmPr|2WKyqmYSx3vlX`3rk^YIhk0bxP(^EaAg_ILl#V;X zkcxXG;$Hd`u>3n785dpHr5`TsWNKP>LahcBqo#qgbuAg6Xgl7HKH7%cVJUOyk2FRo zqwA!Aw=NTZs<7@wi}01`Y*7n4j(MH{X?Nu|153{p%e}~rjfz8`xCgl){}%!GvwZ&r zXB+@x;u3p!!AOhb)Hy2j9TKc8Zdi?jZ~J-3@1!@LuWF?0n${*S@eY*-$;mm%P!--qj;5#!Ach;$cZqGBc*5^6;(N+;Q7 zKpk1AY34iAj0?;FdX%)XkXu)+*10A5kdQ8Q$?1((MDw!jElVyi0vFH(=k63fqRp;Y zSz>*mX(cVp7{m$Pop*bU!IZ^0mJaf+guaIGrfGVkn=|&k0iD=-Mw|enH;xYtxUlK4!KMdiWSK6*>};!Xy{Aw?A2~m5?zJ}_dP+-6GyMJ$4;0f<<&&OQu6+3| z`uhR8DD29w<`_!Kok<9@SoC>fjwjHKsb=LSC)@Lgf)iH>s|5mqe|B_hBUZkbcpm^> z2OzN8!-!Fcg#ci);U5=n2c>=$ki)Q7Za;?yHzDMvY`j~83)^LI(b6^ib_t0gi77XX z#6Fa^tgwkgg;b1hF|7`oI}zSczj58_abGfEM`~C;W7xvb9#%NC-lGoS<#t^vLy6DX z%-AqeP@gA2HyYtv?g8JZf2u+X#9!}VWd%%-o%~V{4!hGeJ4vJ; zD86f(R~67d3$Y-JQ&Ur?`|R!oq5umRN%#Kl(ie`27S6#WIWQEm0G|PcIuvZwr!9ft z)`kfRl1H>~*>sVAtOy?$EfM=@-+oB2BbcX_bm_c56ivhCYGD@I! z*IrFC{eC;j(GLp+#-u$*PvEF?hFF3Xc!z zDIqr&fX!OLrU7|uRx=I@DVzbR7)*~p8$ic{eT4EluW+H2f+n1%z5e+#I#w`7bc8PN zE5JmgS;n^pwejMdvQ4k};o`wZX%>fLh?UJW=1*Jcg@yHvo97k=RdEh=Szs-D$k!DX ziSHZT>UNulFXs#`EL%UO@%ydV=Q-&F2|p_f_w`?X@VVRNetp<-9R0nyRU+heq8jVF z3sCpj60>kd_&Go1$`4yYPaodiP1S;kcn;@rvDxzR1IA5+e=UHH@YVP)SP1e||A z%q0KC#VcUSn8B!x5BG#0bJhp`7`xMunrQmn@zxo5*B+4~2UmhEKTQ0y!L|CTE7efJ zD%8^}meauvrdFlKh|sjD>~ghK8WR|fkyspDY(p)pB;rMau=S$e>G_~k-S(FtSuZ4hiecmGs67ic=|wphZQFnGz1*Zztqxhzl^f{o5f%`DU^Hg_`HP zHz@vRIGzOZUDDHvOiF-;RBa(I3FX}KDcSATDO5gow)d5v%v@c@I<&havXgm)l3y<8 zF?Qt#R0uQJEdHG(5WqGdiy#|g%k^hO#5a&7;4!WBX$Y5tjjb;7YET5@&r;gCsroDS z^x22K!kk!y4%d={J3cOqT@c9{q|TeMfa=}<5#uO5!8~y4otAL{6_h@pwMe-zEx0K8 zXoQa4^4-+tI~iKRqg@r(fir#y_Fcv>pZD}fZ)4UDZ|SKw78cLvW6sYqNuK#a@uo(R zKMiL&MnEPCa$nYC%fX~WSC8ATpNEBCm>t@WrOFq-;npQl;sZk}s@o&e3{?MQ#J!PM z4bB6>vSzzea>V=>EO0}CEe7Gu-=P4VN0T0*a)}>1OmCe~3Z~n+SXf1{dM?uT0bRtu zaTj~NeczFS@~vs_9p*VFw^n(5!o^AaXlOqEY%&TxNRD5t%xuT=vlJMLC~uS}x$7wv z;sAT(`1ens>a_eb2L{fy?sJ?v{%2)Me%Fqz7wxCO{_l!Ymk&H#T!R%3UAb5!ADRR% z!}j{6pJYoDTA2JwdfOLzGo^O+_xIx%5{R_?PYkvh4t)J>uklLI)BYOry?tHg=){1O zuhFCT!K6C#tqjDcR*aup?!0p-W!tyX8dEbwZgr5v^Yuo_FUyPq#DySvfT?UUL0}tTxd~Yv`#*T(CrjM$I1E?}Y!Q zuS^#lWZ2%QEnjV3F1GiEdtJm5WT~^6Z)ngApK3R~qXX3zl!cm3bi3e967u~|;~p?u z^KfTQQLyt|ZjWZq_zteC>;evYe)89aDw!uN?IKs*&{${tq^joykPPdOCl7OTG#>N+ z;5L-EpLh6{q}|GFMEZ*?;WM3mM4x`HKuF z5^ykmVfesl=L~_C>z_}S`iZ~!t_Hh)S=;0qJJi-+nawxQHvEoOH9du%{0-T@J@t=b zM}xfvp(JBBhHAdy!aS`+$B-7HMaXIq+Vw%)E2d59=)eGMe}BJWwg?a~Xg=sNxYwi? zleSr#6ejYXWpMYyqtGp)xJ&HF#{j1|P>hV+5#Vn=*?h`X?Ax|Cyycb$*7o69E>ytT zFv?4qX=d=)FgqT=<#XF%c_FawDglQ0({1_N>^4xTWpwC_; zF%3svZ&y{Rh7ZvqIF-m`jdM;3PmW~-LmouI{9adKu7JHera)1fL(q?FanxR8s6`Eb zTMRE%MXso^u*)-(L)kac9IX=#LmXSuZo4~&6}jW})qfpCcfGaeA7E+ugGQOK`gzGO zpOe2FT_VMR{cp1zv;tA<=~M~G^@st=;e+RL4I{pLPRuCyMv`fy@jwfs5jp|e!VX;2 z<=HHs?IRRWz%JAHfOXyV;{P(iF+F3)y;}c}$KiEe(MM%~obMXWtf>kL&sVWAqw$wX z5^SZ9tmPj&s_u$5^vbFV!_)iE#sq$kt(Nk=3w|VIL&ee8XCoT^nc26JF3P zjfjYtpd~6kjdHuRi1r_TdrxK3*D>dL`FDl#WF9?5>%_;H-VcI> zdq1drE|J&-QP{?MiN>MYCOGtp2MIgl)?{p|AFU39HMn=(#>PE!gk)zP{#~T$85yRg zra_ZF-C>`kePMynaw%*Ymb7g{$mHZ!Lj*}+SvKlsSvB(YSF;$kr1rJ?JUGPq?_y;?|Ax(a@a1u` z{e+VWW#J>1u`qw$(WV24A9AddxZ2%%1~dm`A_Q^$kbpg4Ji7mV9DQVjX>E^^43Z#T z00aZKk-&q4l1h2PlSBI5Nxx2wf|2A4iV#)ZHk?9vq6}I;i>QGL+yMNd>dnhQd~`d5 z{-3FXPFgDXmUf3#3I=UyIdXNLw-~7JlK~Bz8-@_>({SLj>0}uuaF=UvPZE98bzX5J z;NvCY8p_9AZTJ=1yOZv%>?L{Y~$|Qc7a}3blVzOldY`ou#y!{oOVD2b2 z%%YRP=UjX07JheBu}%g$S%o%sWn*jxoP`#HW|?j!&Jn==+f1%#NXATQEmZ3w7wDxYodD{_q7Klj{5__Bq!y{7?2t8 z`vthNfN+kJSv)Rh-69Z{V$3{CSNO>yRascc%MNMX=&3)XKS|tmpQbj+(GU-yH}Meh zI4jUx2o|$ZT*@ezvF4tkqm;MO!f0L&V1X`r(935jMZF;HB|PYo_m&4hc5KUK=)OKjw(s5oYuHM_Ij%#pbIpGqb_)im6nlKV z2%()Zd)xhOf_nXBMm3$0g5Eig@&JC#n&2PunGrFJd?mJS* z2{Ofw(XSpFn9=ty)N?<%I%qIUR@NL=HwD&CCSq#RtoV)-rh7e70Pr6kFt61l-}I*O z2nfthm#QY1hYdb{8W*y6GZDrI+rog{Bd`2=5q#D=kpMN%JOS*~)PAbCb&;;@+$W_e z)JW(Vcyq-yj}IllXe2086~TWY6j2wu(W#34H7&wcO4hvejufN<|pdb-fa9#z}ZdomSw z5b^`eq#()6-^P%dx|_3YT{ds+U||fn%ILX~N+xQhA+5+uRJkpCIexi^kzhkpskAPg zH}&_={WpNhefj237>-F+8M|g`@}yp0#q(8D`2t$U%?}mYn=+^_d;j!;P#abw9IEio zry-9ED4moYU4}m{bjx?|DdQFb7%53QoA#M+-EI&-Uhx+y{(3i4-bjb%>lm`ee10B#5z`aHgPRniZ5i>#J#d`DMN3^tiyY+mZ%W#2K zj|<@o`H{bPY4EHf;b8q?>a~gr>!1uwyu~499=McLJ=y-NH%zUNc#la=motd3BytNX zJi*H{zW0kZ_7j-B(n!NTO2c7nkLVii#5rfc8)l1v0aDVvz_lU{~HCWS?CoUia_jjNd~tF&va?3@EU3#Ru(LbPBc^4RKqF zDum|GKY7rTf8edTGMRT6DfwI{qg3SjWfo^Bhp#I0-`?N}KD@}$6V=}}ljI9__O*Yb zr>uOcL+?rzqV-1RLx4V_`NaHN5z;aH!T2xNUSV&Lk;0wcF zu11h60f%Agm2T~seX@WPZR~<@J~&^QEhMpbJ5PCjph@4S{ZpF?`{p^z=-E(%=WHPA zyjJi`Bbh=kDBm`SRQ_%?%_uP|Tn3&H2~2tlV29+p41rrwty2Gp{P8~opW{6EiClT% z4fiiK9aa-~?%)E`Z>bOo|4O!3O>zD35#Ns zg_~G^!4TIVEah$s{`$ah%$RL@5S@fv<()RU1Deqv{bSF=-u{-lp*v3oYHOCutC@R= zb;Sn;-gyE~ow2HX;8yUZh2p8Jefjw5&3ApPr5$IgdAM1_XSyD|^IiFgH1OI8JP8N= z-Qj`D#NnNvJP+vsq{mU`UEC|d^Y*!>bNB2+#k5tryw9XL&tK+wx`XmHs4HW2Kh}Y} z+ht7Y$>Um~B?O>M75X^UjE%m53+;KMOw)tg|E8}I5xF~2)h*26g~wJ_J29ZS-Mi8ELYrDB(P03%TIY?8kfOF*$GN!S zx=eswzJ%0$C;!!b3b+95zXVy=E@n-nT#6gNDT!1vQ#h3W%SK5X7Mh`_5 zcU5t3*Kxz>qLtN(dNootf>m^&G-?dn5zU`*U{Emfd!YwlgTDVN^pD4uO$<31(0Y;F zRO@;CGw6&og8~>Zuo>*2$V~YQr2E+b1`aSZOptWvp)B{Ll9m^OF-w$d28)#q*R#83zl8TVd7Ftc^B2)eoQKkoYUbmUg!4Syn!5 z19#>&2R0n=fY68hQQ+Ae9~C))cvSd4Qn3e2&-lKh=|Lw2douy@{9sK~6A|nUfNv`T z;J7sby9#<;W2nS?9c5sp8xS?^A6qcFj1i*%vxMGo$jMCukaLw{mG_MC7#9qSNcybB zqYr&j297P45XUk>hTt3a^GXR;CDzb``VK2Dpav8yJ~SEblOaJsQ@aicusx>#Owu1O zGbmqh;wtr2H`mJy!1nL#=q3gQz^QM~9pm6^f~u9&0f4fi-`t6_Z~~Ys;P*|9MF7CE zSd{j`z!(>4HrcF%DxM%9KVuOzp?6aTdyRdxPXlix;qcp*h{wrbmw1Q$Ry;h8HTVRm zr|U~^kyq6Z44oQu%d3lcBljP)-(r0;hr9Q?6%?<3aJ5}3Ts#-u>9(+2He%L@bhx;Q zK%wkoJ?iz((Z(>;VEGJp3l&)GghEq)w&E|d#*nJ7II-(uY;rc1X2mV0L+s377`VWU z&igwu&@c~N?zdRi)~PTlw&*gNhru9 z$Yy;rFi8CWwE*AkmdCyIyB&3vXRMh-A>OtyGlcxejo;i}b|wO-cB^RG2gvBjnehw3S~Q!_?-qJqgdc)msjX?{_6vjy zP-7Y*wNY5pt@{$CB63ajjj)=MahbLH+7;X43$2a=GiC?vg-i7~$fV}Fm#jnDlo1aM z>99!58ThA_g#n3g6`VRC;piu#GzZh`igv%7xnzg!C;LjLye|C9Bt_|4|1yx=z zOCJ5kYmLs|9lmTcA8LhX0r%#43}6R4C5qB1st}XgFz;wTV7I^;sFv(!Z@--NHKbL- zK_4T46}axS86-*nzBDT>{>o{+?;_=jv=DL!W&Y&MQk2`%M%VaJrMJ}mQB*UTNreEI z`IaM6{Wtd&domPXLXA>UNAR9?QE}N)0+$We6rajO!Qsj^;-fMM(H)9c=t47;F0dDC zf=0gzB|n**Rx?8TFGrS`nn-D`%NRQ$MT=x75!;OTca&`TO7Pah<)!%-BL!}Z5;jo_ zGh3~+@lVi>EDNV@Qq9}2VEshPn8PYzjUYkP8FUPVIkW4LL+dz4$uNt#R!O&AL|;#x zV?ar8v^u-?#JH6~3mcT{&Mp+`1%cx){Vd_>8YF^}vu@1w4;3g=oAlx~886SVr_<&R zm)Vt3F;wEuW~Iy=_=f_LRG~USGY5ybEol^4W!Vd2(vZ&iG2`JLRb=<8H;9$?m2jA? zXx#y}WEMn6;~MwC4uI4A0@r#yM5r$r(u}J-n=bTwW~f3HM8~uC>gf2DPibnP>+oCc zGPWH|4QeOqu<;)6a`30<^YWN;a1Q6xVaVsYT-n(U(Rw2}e45W264R5X?pPe!sb@Yc zYl<4eVTEN%4%6Y=>zAfogLv}?R0B>54XYB;INu08uq=tp5Eri%XKzMB=b#V*o!c09 zvu}2XCB>6gy04dbk$yWn3<_)uKRk8iGM5MYiU{O!eIqkT4$2qRDFMoh4=}jIt}~*X zU0r}5r!I`UY7Efnc~EbbAo1Jd^{F*tmx|iGlQPg9D$_sb>ymmW=aDOa2qYBd zyIF^Y;&iip6ka%<_Vpq2;;^!_lUq$qM#%Dy1!Sw0pMun^b@HIb2}V&Q>#rS4qnhdq zg&xD$5oTrIHt97O2~_yQt_+N@SRZ#-)MK=4{kb}588xZlO^07#h!c^(Mnr0^YH zS5^^wFAncF)ilF^*mrSrNuMsEl!ZQ_0v@(}96QYqHR&+B~N(14=r=yX{QxeWqN@|S{t z9+nR2GEmy|H85bf>Rq#j2tO>@F4C)I2Eb$+%ci3JH(Wx;XLkuY2n$AwVsG+wJtiDG z&*t<13WMiw1mO)O!wuym96!UMEWwTYk~+^m1MMQ-x|6(v0WMV}_G6bk?pK6Ngo}0; zG8jN0Vj;Y&Ph~iJM|KnmoI22br`>V&v66_oN^N)*yEw>}e>tbpgriMyYmu>KkWtkQ z9YgD^-1s~+2@+jFU)UfGavdg%axx#2gqxQ@QM$AS?Z~J}>gp4s!R?Rg@b_PaOF;d+$o zP@NXELAPDus4>OwRcYa|M@^Q}T{9^1UH&O+XlTm7{Bf|#XFO`q+=eDMIW4XfM3qFu zUiY$?{kFTAE%Ip7ym|jQ@m_9;qdhRiz@lmiiiV^9aCjkOt0GZ}LcD{9awV>Q+MOhI z5VrDtSoa{~0T5nyIIwBO6`>sQ7mAETS3@pn2|_E*XA4-e;Eo<;JhM8v3%01*IVH%A zHZ_RkJ>5!98b;&X5`=}Gi=y=@8-*5jarVC!y!>aZmdi)9^`9CIsBwDtb4xktX$#~v z!T(r679nbG$=24{b65ZINOIy>v?cgmW@u5v-GP0~Ia$=_m62To(^O*jr}7m5(wvmw zE~y_8gq}Ps4RZ~u4b8h_`p@LL!y}Aj`xM}*uA7U(o*xtHfr6fU-%RZ6Is}{Huy|y& zr`Gqz;Cyy1b19ukM>faE`@YP+fO~sxyxJp_Sk~9oayuT-(z{pPlX0wQBo^y_^X!x6 z1Hil7r*Qj5=NWXgnkxJzcFXpzsnc`1xlPEk4xf+mibzYX({QhP=>h4S!Yk{?q&Cd! zM_DukiB5uv?Os=rvSf&)G91u{Lgork*tsgiu*4=_T&{Q+TGEh;lb}I*pH4X9D1bJd;766VeZ>@vo~#ya7*UCCjn3aajScK^EozFN zQ`m*p3zNUAw|?^KM}k9_NnJcuLP#QULBW);;8e-#&U}s-MjU8-?=tkRjt}A-+>eU` z-3E&K&d}mMHP_u3NcBiy-i}gR&P}hOPd**;-i%1EbRPN;Mc~MVC6`Oim*;{5-r^{fZi;*Qn4?|CkwD0ly$V2n)4L!o1-ap7O5%{% zQsA!E?JbGftc?}$Za_PnFlX2J%-6GzIuw>t9f9s=MP49P$Rm&(5hoRYtu?Dyy_73J z5GMM(In`vB+N*|Q{AnrS>Wh(#BJSK=*Gn#kX2GB33F>xlFB2I8m)H4#k%7e)b>i*a zO8@X5G=_@pn(9ioo!D(XB*1zB3IVKQ9GxlkXW+&QJDRBq_iSs)^}p2IfWI!;FaNgxVMbSz1RaMf{CrH6SH)8i6yzs9Or)gy$QI((UO-MwkI-d7X$5De;ri9BqFhed;Gbs>zycBiA&1zX9whQ1n zoW!JNTVBJDK zpAZOhT!iHP@5*|PlemOwV~Zc5@Ba6D(#$gN1i?mdAaZMJyiK!Rmok>e zi51`duWeJ`W&7y!jVx7~@|X@;P8PPT)*o5vvhXeLqBgD^vM!@i`Ebh@FyrN`zuN)m zlCwf;Gf>1p8>`T-2ZH3(9Gqt-si7-@X#c)Guh;y`(Id~|j6&HDVQE!)6E!gilnE;% zEpC3&OIBkPe&JiYpHLuJ?8t6&Z< z+}k3k2k(>R>aCEtDSggzJ0SLJxyR%>_d0~-MCoZeZ4S)z? zaL?sv zV$J|xfrk2=GlKB&xH5aZ$L5{4>Sh{40_R&+O;Pi;-dIsqEgu5S-xR_Ink7n&u~u_b z+tA|+iGfImv?EOO8vQJ zO%4q;5HO{RE~PoAG+({&Ipmfw_U~B73z314e7ahIP4v^ymTr5ptPM-fPxfx22+SEM zxtD9l_ap-nz&4k{6Qyz_iMvDnko)MD1My>FX_j!OF3aQ}oKz0Xq)u5kVIsB>>VThW zk(>^7zOSmOxi91(jgod#8i;1`m;Fja><@!ws2AUwpUKu)1oQ5)rW#JGqBV6J#R;*G_taS!8Y_J8 zIKp?Q>`sY#VvkR>gLOUZu~F*$laletPyJ{nYYdY4?m@jD+_F(A^9Z_pe(`*y1e*%> z{V5uy)y16cv-cFeScCaMKrlwk5N}~C6ftcY7qaz)aDT+~J3fDT?E#L6f(HIHkXTC! zbZ| zx3luN%N(B(7el8lGMfoC5%<{VI)w9`h=0J@E6&*&?2}mxJ&cL z3sxC^uX~I{0m;^1GpkrWK;W_P2K8hja*|Xe6NAaYO7h$|{`*Vcxi{qY@U6EmK2@j; z13CN_R<#vct8C&3SS^S0O*thrhD?6g zW{Sju@7m>=?l(mATy7aqt^wLb9j77bPfv!>7~GeBQRf-^9mP#_gBfnnB~CqMTaZWU-1$dG}?3$ZLUbd*U6y#NGY=OmXVH&fK7uRU6b~9+h|B*!_we+_Gi(% zsT4U8wrlzE4zZ8f+?amVNV2)cFR5AIZ~^zj$MeR`&UZBH47+jFis1K28Abdhh3?Fz z5b4qK_IDg0sW$EwFC#4q8g#w+ed`iboqU=His5oOh9-juPxvD%06?Coq^P)w@DjV~J%CR@0W&<}^72@<#bLj`>Xcqic zX=TAcLs+}(bQ$4KDF{J5+>zvvt^z~{>fWJPv3LT#c{{oNXNlmdaKf7dq%jW6Rm7oa zEALPV=#M&aJ*k?X;GHnEWTSI>`y#t1=rmsqUee`ud%77+UQQ$D4DfIE~#;65svl4cBw|>MsaHygB4k3CB~IBXd}^VOn_t zcfMaZOZ&8T80#XzN*2EO8MiPV7hx9_6|HuxA<CRq zQZID9`Q(7^i5A2xJDa7bkU36^VM+2L(CoNA#N#*5V^NV@Y%?}7n(^VtYH?Ff4Y0Jd z6o%i}o@Qkqn#mOz-F1mW=fSm$?lQa_gPmJM5UsD8M8ob4TCq8r3xNpa0%ZjPNK=gqhkE+@qe%Yy~=fyvOyf)3%vd_-m( z*kHTBUH@5P)*+ROMOGUHHIe0#agNiiC@-HM$gn~?t)#$6?>ylMN)mNU@iiTQY=+m> zn?Z*UPl_BBp@4PtpNR|(lE;}a;YLd%UfX3P!~a6{aG;0mXNw5hI0rE}jKymrWXGP2 zxg$${h6YArulVN)EZ0=4h^#2eDhq)glT@f%sr-FXRyx@VrBY$})7RN*j|b;pG;%Dn z%P`==eflkR%61VW-!S9)swPi@9)dI!SDa_wdl;x&xFC^bB(TxmcNK0n-HhS%kQ)RM z*<{9gpNEU;g1ss&1}jfXkP>+wIV?b^s~)F+{D=O~vGx5%c3Gc{EuiM2Dp1n+011Lysi@ znZ%L?*U0;S_=y|{HrakZtUbYH+_IdUvILqy*FZ%1W<6?4i@(=)T{-^3z6rEl;+4yK z-pwu8dFP*ul8GV2P>BZptdZdP85anyT+|-7PYq5j0^+4%6EO`(K4B$QaL^?93Yk&q zO2Liu`;Rb}X5YSnM@L7ikFE5pmc$xX4R{{<`Jn*cPZoZa*j~OKi&*Eq&k5s5G#ARP zVwcIX5(&hr*w_cL*5wqlDI4S!H3-UEJ!N+Zmt!st%@(A<+id()PbYAX&%Jy*?+V@; zEm@Al`z&AMvw{TPZTQVvtSgd-CO0X_Ia=6k#8?dnUDc>>=9R;d107C&%(ySN(&Y|?MW4${qRZ&R<3CC*ZP~qm{rmAsH!j0M^?G+{5nI7r(B6QcmBTJ~;!w31 zq~Fh#9?~i-Z6IPrlYjDZwunW8Ja^g^L4r4NZM!%fT=wYju`aUgs9*J2y`?ka--?#2 z3VHb|*C(a~&G*BT+|XbR;`%{Jv6vPzjvA4G9^gAAbn&F5z&mK?%sim~DLC(M`)Rd# z{|^*H`AP1qNtnFH0U8HFomHpmZMrR72+wWJ{^zkK-6$4 z9=6UmODXrW0)Dp=#R4@T+r#%_k);m-XH6B(9e6{YGVIIC6!tBhTm34o>p5Mk_$LhB zr|fV;7gFzjbh-(13HP{rvXG_?2{y_*z7F0n;7!>JRGiox|oMqk{s&dT`%Lg+;3DLdDu8l2oGsFAU z#~FxYVh5m=j}OX#3sF>(VQn%omCnriqk2$O9CW|%%!VexYGuEe8)3GN_|;JM#t75O zpkHriQoBo}MioI6$t(?VA3}S3duJPME{o*zla@EQ7d;>E#>6^(Tw{{50OQ{$10wNNFmJL4hbkvRNUshjY_!Ow=U^xqC z&yq7r@gRE^V388oxQ-#HwR4(c9=@e+_jM3l|X z{n8^~DJ_Rzv8;%=Te|78hIMo79%;|F%zDbeT&b9qaqSKT?13uahc} zMZ4#@iX=L1`prdPx<;~TF7+m5P6*;s!&jJ4GWzbBSm*qiKdYz&V5CfI#T~Ovx)SrP z4<4dODycp_d*PYdXa8YhDdcPqE#rv%pA}I~5<(K0Wl>EgV!Hlxn zMEzyIq3GsJ zW3xeNvn;zlUVP4PvoX||RqO;aZ)L3|a#>t9m710&oA?#yPl{UN&*Pfq(>srQ;Tz|a zaYR7idCSLm0jr_^gsWEd8FbK|vnFfw5-h0HhUweCVucM}<-Ljd)yEcjs6M?5lj}@I zES;=;{Z8G-MV40GfOL884FCY$b8y8qHQcq!2Dw!-GX_w)4k=@`EQ+ zb+6KkC&!LUW!Bw`T|{<`E5jgS1gN4~35#9Ca`le-Vw`GJkj5yrRqc>n=CGjM^K`Yl z79avHY#faig?{8L^HBI^n2~v6{&?ls(16*D47uOYx=9a#B?0!7*%$-x=)735_54xF zaOjVhOivIdiT-uE?;I5XfNTHne*rM~mwWMn-+$=O^-(fKk^;nCF7P1TBbZMCv!p7K zkc>>9q^&}{noi~N+5%fb)?rzPYG@>HwVa=P_dWZ-LHdo?T+`G(kd<-+z~s0sv8lqgcJ~HYjjidFeqQ@50g+v2pife+D8FdL@FV( z_t`A5`cZ5nz0~O*o%-0IlTyW9#zQBEv^++gcpZ#bsKCBDkqud@yM7(u|9=4&1?c(+ zTs9Q0Kzwig_c@lw26W-s%FY7bSmgA9zhGhZG_6)^PDFmx7&CAn=U1Y9#Q^|t^*8^U z*QWKeACROyl`Z$8G+KMNNoQ;86oq%mpSw&d}EF)59+hq7Kp{oO=GP#Pkv zjS*Hx39Dm-)e*~?g?IXZj#8SSGDJ`sa!ax0#Lc8tF9@7Ha3K(Q^_Fy2=q%51=81cs zte<-PSLWyE|Kcl7h(6`30sydfK_xWrNmgdR8}NXgGsI)bVjVK(UUewr-{-5OGh8XByEE7Fk`!Jb+!We^D=|X8=BhoRE|mnVFevV z0bv55`88`sah z4UtkVhJ6mfah9_giiV<(DS#FKi3<=d9{C)Ip|<0pG=sB?UfB@V*3fD78gHk6XD826fKl;(fzdEhGc6r{pfJ-T79{H$w z`cr@Q6V(k@?uv#tydNDl^Uf81PC*w#cYW)OJCEdXcRv>&F2jh@l!m9R{~eSFN>vwM zaITkM!^ri*$rr%FKEsVxQjl5oS&UyGpM*WK*mDN$|3DMEZzgCD3 zFOUT2Ej#Y`(cx+-_%+j7_#sK-QSU8eB0q}_6jg=-lVmHLFCBf5^5`apHeCzK&d;xd zl2awc%w(=o9942jQ{Zg7!U@h;&Gmh51X3^>Gtq9p*V!39NCc`QI{MwSXU@Fy(8vC3 z(yt9(sPZ)d0AF_8@uQ_$CHl{%UH?Hznmd&(co3+9dhkIZJ6Uvx<>QYqyzM5U+Bixn zO2bnWb$SK@?Y)koZZ6myML;NT1nMrMFFT%1B-LvZ=o|zpIv`=?M;`c-x2>9n_iLei zO#z@-uKl*3yhcmB&vY8ECuv=Y2SdimK}P&EZqPh?n9(a<3fQ@NOGD%2+)ZXT8q4!zeAGt zYe+l0kaPqEX}jSJ!6i)GqB6DxQ0P*bs63JzYF&iQjW;{E1uWsT8?&hGqF5BAO^?N2 zc&6Vs;1<);XC)}VH!=MBgYS9AY~Kof4VAA80Q8od-uSb-P11Qo8h8G+bQ)L2OEa|~ zjFbwjg&&k^cA1s}w|8-F!Se4SumeGtits+a_^f6(2(oGMbo6DTqu)Dy^4R}8de2?! z8k+sJP`)k!(67AsjsN*A#PzqCxc<_Bw5(87v|Hj1t1_}dEQ;J@m& zE(8J%N*RLaI4XQd2c>)Bln+1r{-0evd*#7hyBGO4j zK~#9!>|F_*Q`OZ!*(RAJGnqZJF#Es&4j_vNR6#^5h!z(F>r$zKebx?>Ze~@ zt=6TswH2{$wN|OSRY62n1yK+X2AF+em_3upOfs3t?g?ogrSOQY8R@X>h=;^$vQ} z>|(WM5TEw|fYa;s_IN%04m{WBarf-$@9EeN02O%D;Nd+f0I`W;joy5g&X|0GHa>Bj zHa^K>vW_ofK@a}W+v5P2?I^g~YkGQG zj@WyekMwx@x@`k4M}2=!`#YXK$0h)%JShNU1A{i+vP5S}{l3m<8E?)mvm}?CnVvYV zOzX5YK}Y#V;B2o36a+{roDB)N)4^;szz_yUjbj}3Acgc=y3Xx(LwDmokF9chv*YOg z?g6L$h}+S&1_k1M0C1fYfXG3GqkMthl66O1LPlA7*%E6?=~;2+_&Dh5>Vnq2Z-b-h z0GO<$kT&_-ker?+Ayz5rPdak zvL@ZDi8GynglA?0)0YWlic??6HL;G_Tm zL#8Qf^2@0c7tJU+^Qr`$Mg;`)%b}_62ps!-Ewmk22cEv3VZxFMQ@{T_q^G9~$P7&o zl%MCv29P!vGU6Rp!vU50OAbExsRB$Ctp&u z)3-1g?%0l@uVBLHfxeug0>@A>?*e^#77d9DEv9rj*m*!nc7Nj};I zFz1xP>>oT0aauJvjghYnc%G0{I0tGs{t3)kWuP;dfnZE4)M88{-PvBVf_z@I z4Yqt!1t8g&Uc4Ty_Z5>DUTbj=48oC3&%vR!w;}Pn0sUnN1&+r3ptF`iQhF{RLJ|_69jU-gp#GJ zU^JZVwcd(%?yKvwH(yQK;A^h|(9pXoA@|g$rmp;bj;&!I?0@+dXxslj;Cu(+f_K0T zeO*T(cj_Wg<8b2~2(*DrO(1<n?}A96x3v;9silyTSwKP%q#%feKx_|) z1%#jHFs2ni^Pacp9)T|2GU{{GkXpLXgbAian9VexCfM|K69APVC4Wu!=~rCqcC=c^ zz;kufL8M`;*$FArFNgRz_OnESAQl3#Js{Iy969SIjGKz~z{8llI#(h9_@(juGy^`K zi4nx{s0rTux(I+)pET|*RAtL;RXYr}if!!2L<5xb@9*h^?8#>WheQw}F9ZTD;2G$L zmc8#n&H6{6yWvZ4*%}~a!h+F;3cUwqH}z;kmEx2z2Zi8O`kApT0Ho6XS+wNN^PBg) zq3iE%g=hn#YoO1a3rVR~$vOE#AhriM#ODS)gV40=RXFnIDri5j9^Wp~jfnx=G-RIERTnkO*+n}X- z4|KNFL$}=q&VChW^d``0)Ikb?gwT++z_GpWL;1gNhxUUTz~kBy0cv-1#^BEVJw!%O{U6fq`Bdj0KX4=fTXY z9tC5Z8VEgo9_Z?5MK7TVoE`Oe)Pl#=eLUqH55|lMV9G3oBx?b)9i*>CLpRHk4%m{$qlKl%T)cBuM&i!FZ`IeX2 z+%^_S9)BjxxZ+_VKd2P3!!-c5<{F$f4na@zAto4DQwUgM28p?+0;@q_ucJd=Y`}vj z>bAc~DPdXBO#~y$U&mYku48DPC3q~(`^My(1;3v@PB zLdDznGPNEmj2R^`X~m-?kWvlf9Rv&(wRF{feu>9r-!_&4kcqC$c1*|=QX02n=R5Sp zAlkqh0yzD$U*UYo2?|{ZMiSc5<$#VuAEOH2#fnT((?M!>v0p7vw{tDje)I%j**Pc$ zRyM=L6_0_|V3zzQ(kcgExux3CxNkN9w2g%TXwc}}UUtnhvs052fUxB+S2J}QO%Nfc zul)58`5{&NN(I38eBb480#^2bkpg8V!YZi7+2l zz1UR?GHM8*kJCZYxXEChv;e%i1Zdp$4A_pM{m^j~%3r+|TH{I?PriW=^RjNv^sG?Q?d)lzGAzGnE1!DUhq4bU2Z*~@;8g!>=OTnH3s0hvfj%VfTLG(aij+7BOv zQ6QsKiwkHjMvf*%N&u*;bxp~VJF+BSs^&9@G-#C3oX#>3TqU~;%k5YB19 z|L3NivY4sjD1j=fC_W*nj6%=o6ul+$d*K!U4f>(C7tUN_0*;|TNkrrR6dRwt@;=Zp zS8v4OM-aG8AV65BfrNwvz=$YB6IJj1mP*J*$3@IqW6H0B{vCCsmM&l`m7@vF3PRnczl_d{7_iLj zmSFf*2T{>`75ceFeuEUf9T0A4jY%-|y63@~q#r>jKcqqs3oyiKAq|5y(MIdO|ByW} z!Zpm8en}n%?7pj108|)iymegJS@C`)&>mj@pK9k%x#G`Ikd+j0G^@vkbFnd4wiw0& zHR~V7<#aEeD7DA6mc}?!$}dIv9!db zwn$?oO<2v!gB3DSvh*I9GV9Esi1v!YyZpNfWBvSe5Z4Cyd|cWNO}pQ~dD#ik1Z4;H zi5Zxm_&Zxnn~;49U{ehPhUa%87u9~XE%*mDV7Mb^#KC)ONp2t@!A^vSv7^*X7N=<4l9PT=!4fP^azI#6oHs@?|sPJ&evH>ud za2>8>&5;;=gTPj~7A(4%ksoOc3e>TTmM~HKu8FCcmqS`w8bk`xx;p{cF$gMDi6+chzEc3>m)(zcwe}}+1)$KJ zH92>*d(~L=6_VqDoWe=UM#`XXBI)z^zomrpW6VamSO9rwm~!{gYM_UCn(;&Rf)cL^ zrvxh5$`uO$ulBhX%bhYt5@=19$H}EbOupd!;Miyj z@&=+m|4V_|T!kg(%YqdEBl@z|5tXrREf{xpL1u0Vga<^5ln^SxZnpLLs>SzpD6$X4 z1)|E4S~wHp5;LOV0f>6?0Jhc5BIO>00I25T0*h1_hWf2*09XBl&$yF5Pb4r^*4|C4Xizaywfj0OU4YV#zL zcn0O&C{7MnWmIx40|N2|0;v3RghFneeknu}8bc!MfGz;7bTT2g%S8e}^Z!ipV~5c6 z*}ouX&XvOUDe^~?bSlWnvQoom{yEA>5tFZ6{R;&?F*UN~Vu|CX233fH!fJXo*+&;iXpi z!S?0@Y(kYP3I%6kO_`Z)s9Hs%|DQ1KTh|4;Ipn)bez3TzhQF2*#sq-6Vn9Pf1LWuD z^09l`{X4SII~V(2tA$6V5|O=YAESo`4NSnxjjf(8Kgv&nJVn?X!Roi zs;LP%Q&Iro=yg#XnM$1`H$lK}fa}FVkTU)ZY7dJ7aE*-7r+|Ff-Nb^Qep;(O2VBb! z`SY%1=S;~~JeC0_*r30^pVd;@sy=0vzPKYvC<}A|8*UCKInZyQwT70~eX25icB3x= zpm}00u1bw#Uw;346wVw@l#qO>7LYFl)?^b@chsNI2x8I|sBd|rUltr)9jxnV=|%VZ z{2n@;4wCX`fF%!A_*p*zYz76m+LsA~y?#$Hl~&Y9O6Ho5d;@BMIoMInrvOmsaW$3zKlmXfGXfDNIj6uTkdT;c(m{pO4t@O|;PnV%;+2!HeDqgs zdWPlWlk=v8HTIy==*XR92cc!p8-Tl!1n&iKEpPPe2C6l2dO%Q{_N0tFaqL}80^Q`0 zRB-036;S!vyY#+-LlOC59sPB?-e7HAGAGTCbln1}e<}nO8G!s87XZC~0nix?PFEiV zgE-lO8xScW$jvJRyW-{M^f?0Z6_0*y@ng$($e*(^8u10<3fKS;wA_^lh)U~M0LW!A z*KT;6sW5Q{PE4d&2m-&JT*B(|-3o~hjePzd;?Ylyrds#C2gQqS3bK2c7}5@5QZg+C zr(s-Sr`)P*onHaKCIgA+x{$jdAzRT@z;C`RK`59qA6mYAO9An6$(OI)9JV&r1G!}B z-2$6PR0kp7(;)$%0#KRI_!9u#Pz^cyq(Mrqs6s~$5z;Iltp_A4xbmwV0@B6A%O_vl z8m`CD4OJif9*P$KlDd;gUK451G%)CK2Q~k5JCA))tyBQ`V{$VDWXc&a0#XE|7EfEi z)N8-TD@X|oJ|oQJQ&XtQ|NI6@F8CiXC0NAjo%{aE)jtR2`Dq(H@%+!H0LHa10dUi$ zLv2erf|?5aomVIbyf;AAo{vBR@p~#FUkX0*X=QHl`M(0A$t>T%Q?CApSNq6Gq0_Eq zUdA;S0BH{-+$|e?K)w+0Yl0=sCwX}jXMwF^EA1#2)aymyf+>H;5F($NKo*^M2azxI z6qJvCaYM=8)(Wxu=kM>4+5o&NcW@{Uz;7gf)5r;dm{6)nInFRPb1%y zm>kVivtL4SvrWM5?1O&I=*^kbqkIJS(7JP8n#Cb5&=i+%(-8{NGpwwye);B?pt-3L zaMLNdH(L?;y-pX+`Lz6slON^L&(Cpk`RjsqFZ=w5f>o*$Cn*U*9ke-QCbWjSF zLn;_lGiS-d$8l=eJBQj@>X@I8t?{VVUvgf7->+o!OMb7rr-N!YMM6VKC08H;Ab+9= z56U)i5(PrQ$@MJ+1gSO%UKkE2 z1JL}%t1$jM|0_vKDJ(isHv0K<+zxQGQU*7mw(#^j@#t&uF90g_e*FLJp+>~r?TwI- znj;hfE-1d$y!2*>GlD}n)=)*=x5IH0smOv3no36a#dPCHFsLe-{!*IN*;>g8G)gbL zho`h2Cpm>xMOeL;<)A5VD0pEC^y@p#2Aq zym=4%F%bgYe4m0rZEC)C>w7ZD6_6Hz(a(!Xl7rcsfCjY(JG$)h>jezE6w+dN52%J(NDD=6e}nftqh`3 z_xIsTjmRDBZmAXsff7NG`uvn=i2=~U4T^}$BwxPjS3dg1=sx<@0NM#_#RAahSf@3Z z`+0CtnKD==Zf#m}unt9{qeEw5`5~)*bl<7}qObE*tV%JS9l>@8p&hs%z{=neV%LKG zXdUehAzJ@pVW5qb>puPq#29Gv$jFn=5why1hE)B%o!|?dRiD_g-t(!T1ptEu+thK! z&QTvkxo}Z!UC4sa{M7~)jf*wVUPx}YTRHh0;Z*&crUTxA`ea|7OgIG$N^= z1t$PJA@hMRgQ)i7CxIh;g3y3_@Wm)-{*?3~kV(D*)h`wt($2jtN6-f7L-+Z0*#E+h z=zg-`kh8toi|T)!+-A|V#rfyjc*{-rrX#ND#W~uLiV2gHJQNanTdsKdG2*o?6+5A6 z=RYAvK|Kp-za&mjgvgga`iYN9Y6&~?dann1nhs#o==G>o8@urFHKYjSwu`StV^Cr> zV*daZAgSHF8r*#@5JMaSZruJH#4yN+iUj!tCFE1g2Fd>oukw=QGdgK8sRID&LfbDu zrIzeKg_}k~{(>7Ixo9qH7=3j2TYQcTK^SaD*-BT4VNe%y!89i(U%tOD7LxkSPXey> z4@4k&beu!E?c)0#ZNJ0q;eUadF*C12Pd=^*PGN_wW*3!comi?wO^f0aQ-c)-RQ>;e z7zY(w_#z`(U2f?{;taJ4fWNd>dmG2nKwWlBxE z{zYyiTh2^~GpA6^+KH7c^!yNLVPo5Y4#m9X?8)u-!oOBG|$Dfv&Q5n+ldV^kCm>^jIiiH@lJ_Se#6Z`pM^5v_3 z+F*m`eX{sPqrV5ayBP$~f*5v85x)ZfE15tzy8>19O54Ah0`L7}yy!Q~ZRu$`2(bzj zekQnhAN&y?Ve)yvr}~431oJ*YMc?%;(%>L~Iye>B`csssUSL%v2W8ax^K1Mifpl68 z^|WW9%vc6y(#D+pV_&R=E^IR=0&zv0g>&c7HVieshm#7cw(YYsqe!kG_!S0YayFD+ z@;mZ6Vpjm{EyIbIHQ$TL7aM)#dN*u)Hke*AL!-YVi~>M6p!rJdWJPaEy=g?FT_g;W zgIcE1*6^_kw){#u>V@QsSNV#KKKg80hE4$<0jMKsCyxGWBC&gTAAYePWBu76HjxU3 zVA+;#-kH}^KY&=B0tBNzGUSudcj(`Dk}(bx~-ul;5WWoU5}w*iK@5k)zj?Z67ao3%`?G@~sAqPA{1Yl?elP zCnsF;09CringEE2=kvXAq64Nk@=d^P{WoMDj0|-!Dc!ItK^(t+<+I1}21b z$G`oFL#0wH-lw0sW>Uw}jzb$EMnLNlsTl+9rL26^i^*qok)2022if~i??ui06mG`( zQZxjB%8NJjG#@@Ysc3Ei&O0guKgk6_;YLV>tp5#eeF|fZtl8hio&a~Vl~OHVzRA+8 zEE}rUFw*dUVI!Z^e$DznaPoyvk>U3xYN%x)4LS%NN#>j>pHEu(coE)+fUA7*e8|hT zD&Dyd+7E7kv4GxUWlO>>g{O}ol#*A}q>;kfEdP-=gC2)A-}CEe=@y&wo$WQiAyoY~ zRQ-A6oJCszXuFcRxFy-^1?Svs^`p)SN(6!;aI`)UN5mG05-5;D`y^0rqS4<~l8$BV z-fKT2VWn>CG)>SRei9o-6y#|y-@x`}LNevt|9-sBXAC&Ic7{CdfYhs4Xv-T~Sxg0*eBrE`)$B1X z!Kcxo_o2-rgl?Yno@u?<7hrW?*RdNw0aOqM7&FGdn!oU-W!Sh_C&Sp21)3WVy>=JN zIEEXvwVQR?G9sTs(sGFxt9wsB3>@Droj}5%(Zy4Cl)Y}(6Bp1$a>xgrIdwe-=M-;A zrV88URVn!&mMmR0-)NnnR^S1MrURNI!!sSI)NKB?p1O?Yc&>$%6E9Zz{4dLt?tbzj zS(G99`%q(?O1C6d1b`49pIWd5efSyZBB~&QXxi})sNb?WWYSY7EuxAh!Dtr{ub6zE zYTozv?*&a4PEFNlCrsqgzSt1}LOR+6TPCe|w3O!1NP?_z@|9_75P^E^)BXo3n|1+MfvkH4sKbN>!)e4&)m!u0It! zbxGI{r$CT_MK$nLR!&A$+N5(J^_26-9iw?&2H|4Tm5+Mq_4M7FzgQc9A-3mWEsp#d zEX5mUXdp6h4!Gn-FPkZSt+?Q$dG}gCSI(ccPvn;)`DF0fAp%fCZ@$46bx8C29u(-;an7GdjsipwYKS~Ms0pscB9iYO zc zOGGUNs)6XD7w5ERG24}eTA?r@yUfTeb?_=WXgM^kjW=hO3doKG@p9G-^4HXE{1do3 zYEP(qohj)RoGusg`TmpCm9PD3F`!j`8hkPkAX zSlM)9-gG5|%N*_UM?GI+Up0u{Q8gC3+=g1<=XkyY#t53RZ^dTci%pDQz{bo|am`z@ z$y#dE#2IBqvsBEWN-CP8fN;@hS57=t7B_5L)7tXo+v{*+uBG_fcfuG$3$cMtv19jI*6A0ke3V=%C0se_V&c6>d;>O``UHQ|N{B(IAjT1w z`2X#lJxjw-6o${0B+}3*Vo>NJ2*nQm04WwJ_!D&Ntdsx1*~!1-po@!$qf6KN1%j=m zjWJEUX>xr}Y5kZ2Hi*c5AY{0Z!yOoW*GJ!Bm{rKul^Q2{1$yj2f$yz zvMf^8+%>6m3Cb5ZgYc!vC@KXUK?0*_ftf>!4Ht##5*Z{C5LzH-CzvQENGn5h6*1oY rgb0$h8nid1l%&yUG#ZUY^B;Hzh|T;E9q;Bt00000NkvXXu0mjfLINk6 diff --git a/data/gui/soccer_ball_normal.png b/data/gui/soccer_ball_normal.png index 339cf26a4e1965af83fecf96c12010643d476724..ab2a1d2559831a869488e7b1c6559d63d685ce74 100644 GIT binary patch literal 14722 zcmX9_1yodByFNpgv~)8PN=b>d5;9UElG5EE-Q6Mi(Jg|6v~)>#h_ti_2uOF`J^ytb zWI1a*vEzN8IuUP_UOm8}!hs;@0YXmtE%@z^{K3Kie|L!+4uM~2PA?Jfu)rU0ER%5X zZ)^uSEhq5(8RQS_C0m9&_>j_BM$=i<-ptv}(9slfb93Xew6k_HHgquMvUfDk_$NvQ zK@1Q=`o%l<%tarsRMMWt^NQw8aS@T`A+E}fYLR{>28PEtfmpsr;j$e&hQ(iK)i?y93F9>GXRwdo}q`!xg{B-|FiZ;D~D2n@5PZLxY3PP#hcq zb3pf3YF_mHvF+^Y?Be3$SDJR4EO(`WDe2_o)CfnU$R+TTbr2#FLU~^m!x6-gTml** z!C7}k4f>nqwp3iE+dOSqzhGFUX;8mFqE}HrwdYDd;o{@QK~`F`uy_6X_3Ja+rIzOx zd&x&TpZuOf7q@p432;S;M$5!vAsPqaEO1oL*-*kdLbblCk63;C7bVS9%1JhK^xqSW zf0uuRT+B|gQ2m$pQzqUo(8J!Cl5vvMNV&P4zu#}W{ztXm+-nlrrV1Tg-yTCZ z7#$5dYnx2Y(6~$1(oi)XB4PZ;kHF>oyK^>f?!K63OITrDFs)>mf3Kyrb=Oj>SC##; zIJi!3RTU9g6nalj58LC%C{G#q0#GEC*+{jC@WXQS+Ppn{{El}Ownbo2`?B9t|7Fw$ zp`7#TVqe8c0}70XUpR)|s6DiAJtYH6iN}1Dra^~`Ei$mP^QBOh5?BT@DqAAyg^Xzm zOyD4kGF)!+`BGf`!q%1(pGF8$oJ2lx04iqZ`e6Yqx2A^V)km5a#>U1+Udz`@3HrBx zHQJwWbKjq(`9`*>ftmEh^}mM>2%*p1mf=?Ia<$($l9G~2CKRAT*?f5G|1nC1dhaX_**}1M+wVCbdnnbLW&5C0L+!u1 zQ|T{RSy}Fv8!1nHZ`>79d0?*1v_Y5zp;J~PR&3Rs<`D-m5EET2bG)3vP%@|c$;7L? z9pNSKV?{hFezr&4-3R=5%vdXGT|C*;!p>VM=AA)&dG_eY3%P>N;^706^xH%Mj}j4& zy*qcGEqjxM5zzPETpp`{r>vNXf}vu(2{57NBZLKPP!h3?TU!Tr8G;?U+DmQUy>XTGh!DjrsWTQ#cQeO|zT870f#R5r zD0+=U4Mm@OUmRRe^1)wS^+GG>*I&~MTQOF`ys2?dXwgCq8RF%}`QeDpFnZtc2h@TF z6ZtY=!3E~CQSh2z?Yer6F21^Zo};Y3@_ShC6%$d*?b!kw7gz6a8sDme&|9?8wk}%C z2VEE22diDn`y^6F@XL&g@a26smc#)8$QO$th6rB9T~PAaS+{8W_Au73JDKyn!~N~~ zr^-q~h{s_?QuC|&s+-5)NhW3>r32Kl?YtIBZPx)d0udG(VQgQ>;l;@#L`gDyyS1>e za3+>c?C&nF@=;y|Ep&Q%Dl=f>i0c1D21SB|@JmUg2`@H;g%Q+n{jVA9lMgexZWBVE zKYs@0pz~ZtV4Q8>eogrxxkSL}VIlJC9$^lZvS# zZd|?xLG#Jrl1?f{GB{<~StB4)w+AG>orc*0*gr}2+6{ffY5-`m>*rOW+vO5<=NMStz? zdcRLiLl=GJgvU-rg(xyA>bThUIdw48f3_G5yh`KK)1jL=esodf+@bkwwOWHe_*Pw3=`Jp?_p?fC&Oo9WbhZ7S4y3MY6 zg@qU~F)DQ&#{-$ zOM52YE|K9sKR-_oCyq!t-^Np>{TUYeZ-yL8Ezhnf(8>1_X;FX~vqls2wNw!!` zm?4GG{mt>juV2wV*Jm&x&R?1w)2v+|KkJ}(`aVm{%4NeTRLY7>WL3^DD#AQ;7wusf zMs*g1?5}U{;(h)jzcur-@Ne}x7YFspPc9)?nhDm$a{$^tRW--@lGpy{Z&N&YHp^|a zrRrZh>ueVcYUaQbTkw6KcGgM;XyvU{a~g}#hcz?~GF&>}i`Au4bP{US!RHgT&W0_tn)k3}fD+WUUDXY(WB4Z5-=J9c}URuc$ zOG_Vdxm)7IRY}oZo5hN%e@{+lwC2bQHQHNSOG!zgj*6Y@lcAh^0yX->5NACk^fg)exlBRya$=DE@ti|EZO4+vyLT&H z1QOD4;ye`Gke8N0Tt3C7W@l1$n`x3x$0N_yK`D^sr;_2P`h0i|Nm6!L`?j~wo)}Yp zQHDT|Z$J1soIPCU5v=*W?0r3c0{pvOJrV&^5WN-aX~`~ zXo>0YhQ;G*x*LMUPBS$%<-Qt1o@+^k9zbad>wN>Y4;MSoY&`G>gOvvw7HUTiZcb)| zhLVqu7?_#VH9J!l6TX-Zy7a<{pjOryQ&Us->%ICsOR8ISA)3O=ZEHnVOVg+Q3d^R} z+#>THJ74S9ddT&=CqBI@X4#TIc8dIWczyd{qqGUTJp@l2(0I_r#(LgzFwh-iNTJ#2 z_=r=IOF)c-LX+3n8-c)XyZZa&<~Zjb%_03zOiZlM@}%gBe6gn6?XtY(==E09$38l* zXipM!6qxWp&s+SFAli-!-f$&34_Iz_xm~;=MaprAF|NX&&4q=9D$^k{s%*p;vzKN$ zX))N9Gu@_Mmq#1y-&p8kKb4j~*(}Vh_?t8Kj5_T1f9M+kRpgaC7PR{n-V;U6Oo#>A zH3J3S+v`UJ4Nwk52(w5(6M4&UHmt`=j1BJToApc~gI>3Eew)nS ztl+~<8wLs?2Zpo#qLAyZrnXk+Dvz+Ru)^1W zZC`wA(sShCU-nTTz<|hRbetZ9;?qXPGrxM7P(V6TWJxtRG^7FmFeaTl1PaKf4cxG< z=Pu;R6Za!B<&yXiSRS8cW={3vUsmbzB~x}E@I^1J>P=u#`jfiIy=#_hXgnjl@Irwg z_p9xq?M#TV^8_GTXIIz45z2!k0zyqXqJ8?8V`AqtWSrW;U`2jvXb@670AR4yi6%qP zwsWl~s(2-W^_^fCF;stb>+4o02QCW+=SCvEP_2r7<9e&Zvfc*3bPWx2;S$VDv-G(G zb1pG7L0Umwt>kUC^^de7mF|Z|(oFHSqEM@%y0S6>AOaT0E`@*fCu>Q^0d(ciYoTbr z-Lrpc_e)49Yn0TGH4&l85KZwE1>kXjAEcd}_@*AOI;0+rN#nZ1ekCc^Nhas50P3U- z@XGRou7gN4gj@pL>Sk}bJ*Vz|*J+}ei8EfAB8Tib{d31)?Qwu3kPs_9J)HqTj4YL# zm~!4fyk$PA;PA+(@cv05VC^(t`1^(}I0Tp6Xe3>LLd2EBMlb`rXk=i|^%Llh+-(<3 zTuoa{pb?Ho8hq2{YZb0-Xt)Tm**4F_n+L6-HN8-U9L^gyhTF~KiKzpvAOY~;e@_$_ zWUdSm#7cK2I79p|q(|fB(Dc0zWzx+U%$pZK7ONH+U7c*F@>>$?Hp$?-_KX+DFc?&6 zT8Q_5M+rE2gB5F{C2MW{#IE&JeRr{$98{+RchSyjlXNWObrxdmpHowySn1`~WSY>U zCXZ#hQtcmK^8QmZ5Js&6J#}d1dyt<}a1U{58#b!5ZWzbVm#<$HVWC^Pf?WlL34HWn zKC$*J?hn9@`ba1Cx(k5+N|OQN3vY2}XXllziBCe_7d&VG7Nd%Ce5pXS`SRsUM|T9t zQ^QWQXnF}c=2ssJP#A2V6W~vzoqEpKm=oAFZjd2S3P1yu3wuaXMcPxV&^FVkt zo2H5O^+jOSFv--B4OPGl1{gvSw|{7mLK-hRa+l-a;QZhxA2_cv?n9y^FeZ6*bz}g* zv9Ymr1Y?uD#b0Vnn!)I>H2)bx{cNqg`QJ++8YH~JctB;6zNs@by5#2S3Q+D^<7Rfy zqFq=%n{%L|L88scr8nO-nm_0JIvakx^|yl2rf_Om^n0|q{8hkFX!4_YNZA@+?_4vg~aOaq90329!FC@ z!{5ww!Y?T)d84Hj0Y3R#&4M5Xu&2tX2S@aJw`QFGfqLn~uFwbamg0^or*jZMGuqnP zE3VCl7wfT73Gk+`F}aj-C=@Hlmna@EgZ18+hY9dYQ!VvjJjOsFt>ohNyQ7UEKy+@H zWfW^n%m{v~QV*}uihENmE-oU8$y58K7Se}`-uvM^;TwvcTueN1EF0r_QnT%T5`X{x zt+bxu5|(5J6mMu~_4smpax&=Z>gR|A20jfIz%Xv_b8sOOtzN*+c|c*iStD-;z!NOb z+6czJ;vW9`Xgaa02Ye8gEfm$`SYK#~BSJP(Hx>*FM!BR8 z1JFsmlsRreed5rr#d6vlRUB{vbL4`avEM~Oo^6i+#CHJ{Pz!C51vUT zc>3VE?#+jQDKY@ac(%pENqcFaoO!p*q}*1FTA5U-ytK6R5J0)S+FDY#B@foX?+m)3 zqcP%oK}$Xq!EiMH@||agp>#yTB&_IY&=08N{UAJA0AFSVHG&%jMi30{2f3iX2ckFp zYud4(0>#`!7v(+KS#v>MjWy<@?GqM~UCD z-af@=i^2PT_tz(0CW;K#Sqcm*U2^y7_kueak`}$ul$wBQB*ErBi za2FJ!?mTopCn|s!;sN~gzTU0}lr!X-1a01UJog1q8}L@15vdfC#g~)`4hi7%7Xff6!(W_{W*q!5Oo38I6J`@V2?L9 zn-YDOn>os+tV2gCh=kFjOK(eC+jNX^{yk##jcS1$donCOz|yqezgMN`J>MU=4we>B zeJ7<5?LkYpdGkYcM)45KK7*Oh8c z5gPKai83hsV8Dvr6%$WyrEEN4(#KCwudpuLTUkvFGj)ieDags86pBI$#X1kg7m8T`KoVHzK<_0(p$0fDo5@nyjA za zdyR%3n+_F)b({rWm_t0)(;Ps@0=%~NDI{)xS$CTDqf+}Tc)m`v*eAyVKrib9vnX2t zY1Y}K@d6oY)hwE+8z;x6L&%^5LS7g=0kZ3xjijTuFUK}D2_ySRO-*8;f58B5wgAOk zXBAsJP*vaP=_#cN8g%%R7N%nBkoMwUC`d|n>>av0%$cSjX*2&m(`r?ESJ}WICInfK zFqAYX%o0N6u-VrrY|yAUsO(*+AL4?z6*R9qYpm{(eg%e3|l&0#)08IJAt>jNe@mrXi3P!ei7<{YvEZeb{( z=2vbGrv-slI^l*8y56%192Lar4+AK?X*lyy;%jeCY3Plb3)7Hv+s-QtFL<%rm&3jK z<iN|1w;WC=fa!)a<+@mCDC{NKGsJN3qu&Fm z!Y+?TqNc1uCbt!c-mnB@VE^~ohGAvWG=ryZG3&V#OQw=}c4 zdFixYJxc%WXhQ@EJUEc#_>54*!gHCTFxMRR+D_Fb|%# zO8;y-7*4vDQ^g5D$C843|Hd&O&m}a^ukOb$&~WZqeswNYpx2Mnt+b7 z-UGwSpD)ELT6&pHvz{7bI5Pf#pSX|$+4D_|!fqLj#n0mQZ_TeAP;NG>WXWTaDTfKMd;EL4v`O>kU~f@6&HR&D{u;4oUVR+dT21gVfL2~u zT3TugGx6em8asx-jt7Ov_x;aZVx#Oi2oIG4q6p24yY*m>#{$gIxz#N!W`15<$9Y1A zeec^BGSuSEhdrwr5352MiGY{#b1&^7MGoDyQsI1dX~!}4&~z}=uZ2ta7Z+lR$C8bq zl9_7P;$veVj}xXWfVOwWkdiMFYleh`kecp>U38*u3+Th)H3JWikmCR97sJAH-l?-Y z=tZ6V!MYJ9wh`8-GjUulyA1$bAvSlmKTWj#&8=D-Q6_qg_`KFmga<&3MZbKlG4s zLNbyKU*t7gdL@HmG$1LjUSR^BAWfNH^4Mj^484A!i1doZpDkdg@F0qhp21#$cC&pMfc=^QGdeSbKFqxaEl;mrWYO# zfLFd$NlG#`OgrjV!F4FXexo)8S}JH2#D@IKnBF)AZqf<@qP~4vnE2K~|mu|^u8s95s8OCT6=ntwJ&mVy6syGn(X9|5l0aWuH3pzS_s-Ug*;NYWpQH9^6rO@XLmI0XrO#Wq zBfN+GPF;NskbZVclA!*w)=gW~nwBaBUh5qh+tM#ICQ#UTX+>Kk7#Lzum-0T);$zav zG3zM;dk9${cEN2aB{u7D0c#dGQ?q-n;2o zZ~2FcRy?0I=0f2;M*2sKq}4MyU&dP=+N;ZJ=0z;6^YsOGnAPM}R};s}!9R<9*%3Z% z9IA7dNqSX7hvGLXLk-DI$fg&r0#Bls*6r7edmS?h*+Cv#7Q*nho~1I~7yAEOv3 zM_D>n5`K!Kzx_HOhplnOmXjay7X!+^FS}-MF&&E;%mR&%m3nW@N76&m)2VRG)>;xD zjhN+(%_!KhK{WV%>L7RF$|INH>9-7>cMdv4GMO|Cis zEDfU&_ZC{bk5%NOiYc_s89a-rv1Fh_YYa>uR!FFnxW60&&=Uz!M=+ZaGKDwc?#5)Y z?)Klbk+wQms@hM7@m_p+aP}MAXb`b>)$_F9ts7b=($Q~7DAIj?&rtyZPOL{vGq%yxCCM+gtWO# z?ft7B9naG8o$7d?pXc_!8a~ug8VLAfZ$s);Fm{lq@S$JWWS#f7sTt&*Df<&eF|-ub zSu>>x6h2UZx`2@!L?F&{+g4I?GPd6RImUl_*ziBl(y;qAMHe`M!TikkdSQ7j+v{JR zk8e~@!qKZY*_Z&&qxge81QHnIH;1iEAH~luy*X_pae(Nu)Jqj0jSJ!qkJ;)nSbm_C zso50z%AoK^u_A~U?QFp$TpSH?Z{I+;>gxX8T1>~2s+eo1g|?MAx;SJM_;5_ ztG`EhCm@J^Xt9{!7~>YO;Q;pwY4ggx-T%Q~txr#%axaJtqBh~Op8%0RN#U5VNV0Q8 z)S_pwuycEiitlW1f0rT<3kJ0tNIl`9iLMeEkOy%9H%WW{GjQ>3X<}Fjw#)~sr&#jr z5O5)T7-dvE`!O|*<4#G}(l{PN2LDh-$sen(A{wTwWGpS&b)iF0qSjAm^k4b-_(ZN; zh*lczrVUq>mb^x%iRvMYkOqeZ#ZHKHdKiENkK~gX=wjEG?{AkaZQ;1@rAa`tfC;x540JlrEm=or?B0rI}O8n}XM%YXO|O_#(rEZNUVm zG(q~qtk(ou6ddtWgA<>$N3)zn zvhmnbMod}Vdhw=Zeh(#bnFY>g&8TqHw2j_W+C!d6f$O#}xhAM30A2yHD{M5gYQM+m zsm)w9dXCQ-CE#}eA%>`Yl2r*q2--{MO0SfyAe^L*zN7r)!K0Dzr2#F72d}Hc-uIX~ zDP^*KQ>7@{rgMltgDrhvxLd;!^aZ5G0MrRHs@zfYqtZ)$D4JH}j2mRWVlC6v`*F8G ztEYHsghm_@yqyeBlkCtQ6E(izm%I7@BuxwOQJkG{1nE?Uq{AmHNSj^vUV^9(2z?-7 zwb?L5rq9_Ng|IW*p88RzzgIH2@0(4JnYpl8yjW6+OL=K<4<=(n169 zL*5;F%d#YpgIkgr3SLhX7P5f;s#-zQ#$y&T`jaIpRjSMt$PXYC zQS`aXPHd=+6@|8W(h@uY17XMA?e&1&Jn(>JWMqK0?LV}UFVXl>nh7KeMEJj+--}6H z&y9h6QydDhZ&8Bx!m)zlOF_c81^}5^m9x>ttN~37_(V&RpDqI=;S&F ze@3%JauQAFhNk6y;AaDH3VJMisf$bqKZ!$OCoFfK1vG?7WeG*q>w?~6FPtiml?CrM z*99BFrYiKqD%NR!M2{EOw&dv;K$WnoXQsiQd>Y%^D+_jk z;G^~y!HdF7B`vxH769P9j|NyTKo}9J?Ss9Kbo0H=b}z%yys^``SiII_o>^rnZIatC z&UPXtgiyl*4rcPFS*_*-Dw#kY09zQGcXy#acADQ2DsrQJYCi!b? zYi}e}|L`RdLCT_s-OWlxPJH_7SQPlQ!oh&wt%Lbt_rgh?a-6PQTaV*f?v0@?{uClt z$+TL%`a6w{&b~789oKdAZqwfRHJ?=@-=XNA^&>;3J75`huYcHGZl|aBIU!rFSL{x zVbOT`+?HKUSJy{hN~()gagycIyN#3u?AvH60UR|oH4v_{u(Oi|{S4@Sz~TYgCVo^8 zii`?yI1RsPt0t#n2E733N1Meafv<(cJJ|wF7v^SrB8_A&BwCC|+mZbMa~Bu0_sp}R z5NU1O2+1=M{+Xm^s7XTNd;Pm3a->Zd8s8M$Qd8qJcXhg>{D(;cO1q~W{sCfd8bUU+ zl^>rP_iDmA)(^)O;{PSUPw%9gK;9JaL+8*E0I-2W!G8aP`C|+yqR1Xb`}g5gx{Xo> z)9Fwzd(@4jHNWsXdXRG^GUYG$zE1g95oa#^Jfr0>cTNdG{PB7!Quew^#m!d!@ z;CNId)=u@w5bry+y?_7yaozt;RQ|CTq{wCtK^-M(rFY}=^PV|VR*IJh)a|w^OUv|7 zq<#-HaAeBoA*i@70o*uE1MyuUAOiKC#rgaD^!DxGVzH4lq|1gsROg}S-uFEhxvGD| z`(U_jd6|LX{P)-&5K9KkLl(3;u7(wqhQ)uR5J;X}?#xu&mr6PpCXqvs9IzkcK>`uC zojwa7iLOv?Ii`4Iv-wCbk-gSt!eZd~r{WkLcCX_+_9sWw{Q5##6Ii66;*#Gs)(&kD z#ibOcJ-`#Nz(f|f4GvSUQ3Ib7KJNg&hexv>zC5#}p6$#Ap$OmI2gR)rdlZ%mjJ^UYz++tnL`ab@oy#EzlI zHtD)>b9eX+(poo(?jR}byyLy}JQ)gf%NWB%+qJ&{P0?vgEDWT-q*O@pXoTXFbQ6Kg z?DD0=Q&HbU4kVt~I60?43gT-5{ByKm6$4?hwxe>H1qw=RUl0fv6z;yrM4+0W25j9b z9Aoafu-v2M&Pet1L?;Pgw4ANK=$LWzWy}2HT}>ZUXzBj9xTwD&%41g(^+~R9> zH8rt#Ih}9DefX=&c}}0jVGvXfRHO%7DLeKp58~xTcuibO1Ai4_u9u?5xQQ=M1Z@ zqOQ*M)pCJb`V}4orHcDpp_ER|Gob`*>6y6HcrCcMF4g{FpvH3l9+eK&Fsq6ZVQE58 z>*P${8?1P_VEms4ou)%0ONr>L1+AE#VIF?R8OxxshPnzMbk0B+cWi&L`F!rC;8CG> za{&80ZS9iA#eW&i&CL?Lcv7osMPGb27PLV9OBs?8^92iu2bumk>>;ns+KAY9tx}Y% zE2q!F7x^{38T)`)R?ExFOPgj*gDt)1cLu*YNoDSXke3$~_gCCB;@+v+7Xv1|d-N|r z9{&;qdVDi=g&QX*Ex}`fE+ZS;cl?UMX@s0o*2X@-g^wGcP0$UC>ut+RM971blRl=k zeov7~@Jty>{FVUnEDR5|SO=z!lKVvfV(vr|2EhHU@Sh8PvJYC`zcms z0guHvLsJGe0S}5Voo2$%X50n+`zsAV974+V;ZG7~+wRz3#kv5zo7D^!!#902S(8b9 z@S+RICwdw4Q?-)v?5moiz%EfS-m3yR6~yPalS|Xktj|O>7@)Zql4}QJ;3W=%D#upc3Kv=>X1h7a?Og`q&|9<}HMnfrEw6Ss-t!J=6 z0#@m;`tlM0#*(PM_4O@Gw0VC_7`;+U5EhWnK>TwRME6hH+9jcI^ufMwnjEP-hA6VJ zbTNSN0eavG0;ElUXKnbbYgkYqaM}oW(QFoXL;{}TBppG>%M<0CTg6-OB9CT`S&Rr( z%rx-K=x@Uji5<%|Q~@0BCdHt)g28`XQ+I5OGwTj7r^EtygU9p40?ApNPYIPd+}96< z(1Q&r-i45S*XAHAp#|ZmP$Vh>ogGj(b|}vDd9md@!TRAvWE+C%*Em`Z?0`1B{{Qcl-rk-1(fY zyi6-ieEj1f-YEz9WR4eJULuRTf0JI!+g527b{kc39~*hoZ&|~jJhf8wRvMfTJ5&bP zT#OVE7VtAn@fQ-2vMdlTuOFaA=I7?;qX6^(=$FNOcMKpGw#{rgWj&)Q|9wO4rB7jbD=Yk-XKE@N>ktWX|;`0@(2EaKt+YERvM7ns&B$qw0OlqS~2<2M9 z_YF2jv)HfE%}`+;EJLjlg^$_Ufi1#*D~}VQY*jkBU9eD-u63L*N*x-H!F1LJ(z+m{ z;tmk>#O!SN8#d>+Gp8ahx_(6x7uipI_Q0hCtfz-@!n*8>ON9MR-@TSa>t$?j0IyLL z>=BiLEY07)Tgk~_%AXPEU)|FHyV8rYbwLlbFn&OvWM_52f$p^*BJAOa} zu8&Mi+kb%K`SSjvPl5{rcho83+pQYE_vz!Fi6?CSsf%Gd!?Xk3V*nbY@Q z)n{$$YU{+P8Thct2`gS3;eF>So$|Z|kF&| zhQ=PF?|>jMB8YYWDVEt!qp(`0f3}}i2(r=_cx z4VKqkxe;uUN54S?X*3H6_fcBf%NRcEf|4wr=ZkK(;EfZS*gks%ocKQ>0beM5;VrK$ z5P}Xs9(!#qidz;uFAxRVb2gV5XKosTz9^at#BW` zlk(TQM_IE>!o}j{{9!P;(iPxrt)IM6VI#O;on9y=ZarQ0(buGF=H)#xw6cABzFaZw za_g1%X3MY7=&8P!SM$)SgPe(%YxQKxmnW%1?ox&2MOsd|Y=d-BJx9AB-IFW^?%+A+ z$Q{>sI~>D& z-Fk8o>V2PoB0&Z*X@9etk__v5ikLZdg1y^m(u2T;NwEfA*5?N@bu0>C8A1pdCF$~)27&(vN2&|P literal 11275 zcmX9^1yq!66J5HyOH#VKQ$T3}Y3c6nSXvrBL_k151f&t98x$lLB&9o~yB7E#&%fs^ zd*HC|zB|v%oqO+0oR)?%4kjfg1OmZ%_40)_c!mCZqN9SJebN@g;04)B{*^8|cm$(c zzXQKxxW6>?fFh2#26gs*T= zBPHa>mvVA)mXegv7nh!k7i!JDz>i9!OTWx>8wF(-pWsQyQ0UA1`}eOD2?+_Bg?4|wpNRUCe|&(j=u$==@H&THN+`=$6& zK}!oiS}vy>g?LS%Wl%6lDH4lpj4Fjz#x!JWNU=esn7h2Z{HD6R+>r+=o|!SHcw@{4 zEiJWs+TGn1D=dtEh9@N`$LBa@59{uh{i-`X*T8{gHY-AV`^3cYf;YbGQNKZH>gwqhyv2$~~HmWgG3EPes z{Oo!ZnwH_^#fP|`Z{t*&bv;v8HaK6)32x3;_ z;YF1HYj`-Qt+;qgKFAEp;Q?cZW@hTczzx5-UI`2PT@UNT|CpL;ep*pp-aC|W+}GEK z1e1AU={;Fm03%VeuX!zj{#ZTo#>lubfIeX-uo;acwzr4AYgAD#6!xLxU0_{ z#`f>J$76X`dRP0iUH6MEHTU3Q3`dXXo;3@L27%m264|EbuOq4$A6@(N$)#;iJwi&w zeWM?T)9evcl9H>?UaHYB7xrtb{v zZ5i3wF(v$toab6E+l<*W{vIwgxw$o-s)!?BMcg_9^ROR zJ;ZSt3tJ5&A8J)he?aHY#%}XFR`m1~!5R(NUioNijM03Qlw`zdk`>3MQ^Fv08A_g{ zv_(utCbhh{X!2{Hs|DRp2LTz}{v;!##HdlnU1U~N)C+pZuD5vb(}S?{ z+zCpxX~z(y7%M9}8ZJEM*RPknBt=r&D=jJza_m4(Q@B~;vj;juLKj!9|xdiMwDW6XID3&4Pb92z+&U@1Q;4{AWD$jYO z+1S`_E@zQWPEKmaM-YpoBTutaC|J&&^9jjo{ZHi+DFkpG93D7B$+)feS3>;$T*k+r zJ-z?%;b`ZLJB$3(1cs8bvZki$ev`%dcGcxnSsH0MFn3{ebiG zuU}<-e1w!zLc8UO(2?=cAvzrL-=(oC;V7gc*<;>rUPP4^&Fdd!Wm$&1_Px9X0c3-}HsYRzU*NJN64MrI}wBxAMV*ODd7kPTM z;8th{TTWFe{&0bYO}_c3#cSs%ARvIKqMZF2vT8;&ACBvQ2$0P(I2e9S8*=e#7S!zN zaz_w|55r=s&oCQ2Bskdp@07!^Ka^r*J6~MO8djQjDH<-Nj?h9;R$blySrMz&c2pTZ z?!|le>FH_zhs#-4QZlj*BO{~M$qBz+GVUfwT3Q;>-Ie;i#qft?tAu#WDDW+qgq;mi zTE%y8a`wH&H+Oc{CX}BPD<;%RsB3E|3RYVB1j4ENCnt$|dU|di@2hra-5{fG2Xm)& zLqqXtK0cSrLPF(?xE;iw+{+MOC@af|`MckJR7@b{*dEJ^Y+5mm9~x50{PIRyJEFFh z_hQ$B7Sifa+G^pEeyk>VzYtW->q5#ZDM?Mnof|7l_ridD+7lyM9-EwZT!+D9AG~c* zy-Kfbgs{01<6TnHTS<_#6t7>uegp}ID@d7C@n2o{zyKNmJ4FKnQK#d4$Fml=bGBEg zI)eUr;pE=QZ&jYvq7>=qwl)c?n@BV~!D_?$Gj|V<(-+O=5rfZ3>FDX#6tdN>96da~ z!2k#pOzxSaVdIGB;flB}c5e>iogZ8vsC5Jx+nbe6Sff;%v?RW%&|6*Yi4^tP#!IBS z#(%*;kff}pmT2tkEWg8{U!AC1R^HS*sIuyf@-8m!!E-FPWis@3%MSMWGlN}yQyd%) zpFyFGgWQB({dtO;oyjkVRHaX*7E9DO3qf(34TUZ)I? zX);x$40{Iv4Nt#JcLkAm{>e6LZHk#rS~_&_9M&Z|ys$8QU7T4Wk>2nY9Rs81R~jQd zF(v^qaUzI(RID|{Rse7dzjHTKqtG5@C)3}j17LV3))K5jl4+K|e(^G~L zF9O1C*7?<}nfKh?!lDPs{Xarcbp%UGOVgoW{+pwJ>UWTF$;WA)EG&#-Via+4uiX7al334(#BaIH zaddQaBCl7>l8?JqFe7`en9Swn89MJA84(c?DJDgOZ?qy{ z@@OeV-N$3_q;Gib&$o|xy|%IKY8$AQ+WZEp-v;56&t08CuI5dQjT0*LYZzE|*sI?T z%PA-@ZhDFZUTY|>bUJ|htUPV0gl0Fot-uVL-4WP1I1bpAb#;k5Z*7T)<9Yiv?DS@E zM?v>!>8#~_5^5tCC|JUzOCQum!_!!K*vd5Hz($oi>XMmuK$5(o6A~gZG;E_!ALJBN zMh%#96dKEuhy^WWV=Qk09K%m=3~3+d(HPR+32_@DfW}qpy_YlhZG$CtxJWB%ZNaUH zj)5V9FNvq~Mz_Q7xcc_z&!73?x#;sXkl&xAf@?OJq@{uJ5- zol=d4*y1H}erxp5n>FQBvQz`C|5WPT<~|&hYQNbi%@39Ly%O`@{}2JXQE$6tSy>r| zTUQ4$=wr`aus=&_vG$&fisW!kE?cA6+uL^wz|@920xwnEAW6w5eg+8QK7TLnoN7i5 zPi{6QgAov;qoZG{wynJO#kJTSFTB|+Jv_O%K<>6Z>yh1N-}d^hplF?wujQG5yCgQ%varlf2sSLxswm+a2{(fp4D1O)!@)2BW_ z`o>sE83ieELZ2Q)KvRU2Wed>r@@kDQR$_H|?dXNLxEzWF1z^$qN)csdwjKUE?PP>j ztInRCk+B8g7~plD<30FOPo`ZqCBN|uW>+L3e5hLT^M`lONOnCnHyxTIHQxK-El+G3@sUSFG3dd8*RPRW ztfDk=d~y)vcLa<&kr5HkzQ^Xj#Z1l0YTnE9`=H$TT1{y4M;y^rtq1zU-DTg^;lkS1 z)=#mCKAc`#|4#X)5-GTQOw#wf*oXV;HSmt#XBUfJJVOc!BumsGTS$!p}9d_r1Mv5F^moMW0 zg>6SaE{F*WyNa1f2b0JaXt6Gb-EQf_E2SJA9Zkf~$jHc2%)*x3ZkHQ{A8aXS^DvTU zyoH6vJ#r=Ul1Ml{Y`_B?2fF-@b(=KuK0iai{}qeIg93^;`|M&8rWF6v5;8@$ zHU!Im_YVDF;TkIS@SxvANP>Mf=6UEqy#~6k=}VC>?ZF@%3xLm=JtNMxf8q zYGY-qFESGm87~%T&oWi zKw|{0QR$AENttPCf>tT>1Ri2cK8ODlkUS52_a;kgZHAdK4~sKxqXzo>xf@AlOArAG z>JW38C+dWF^;C_f2%N0N(pv70)7NUGYnGN`nzjm0rU3HnX6xuzaagxY1jr2BV~pl#_3ZwbweXmrILnxZLeV8!?bC zxU6%-6BCu)_kJsuo^M6S(slnXK9*-rrw5IM6fhKU?rAkOFk*=6+`0!FRN3F(zpg6` zGFsHqLgBSD)?a6HOvl9Zew!!lB|gO;jI^{o){WD~7-zHKQ z_$f{Kna^vDETDri86R$Uo^ZYy5aY@AZo*0+a&Oz16;+s~MD=S-<>cg~`T3t&BwFu3 z=etdJm~oL@=*3~D4LoehNM;OZLPM4_pQ~L%$Herd)HHsnVXFs$_@52hb!>Uwk~7OF zp}b2>4EFpxH9#!8kT9`-Y0%*KoRrJxH)VW2g$@6tGoq!j-2gExFpY`KOj^+H4hd?& z*$*(rCB_RCR6HS3y1}_n@C|xOD*jI_wa~8h5IQT+P4B{l{Bdm#e5_LHw)?M=Eu^ZGWiSOtUh?~TrDS=^HUX*mWlv21~4 zf$k}U@1b^}$zejqr^Tt$5iee8(o)LW*4d6aj(k*nLhA`FoXkNulcJ|b05Tx4y)s&( zG5Zpc&NQ<8s5q4CWHdDD`|T6N1*)0c>37$(=7s?VTB&&1 zYBBH7C&g2*rkIU|1OdGQc82A5h#HxW(bd#rvOP1i=9v4f=QqG2JnnMX^%>gRzkIh* z6+}5XJNrvZHalbTF9jseu^;U42BOD~XsM`FZ%b2`H_%2?4pqbV$1-G;hkk7x@9e}{ z^u>7fJKzDpn`rS$9Cc?%L+|XA{_a8Z5%JenXt;bKKC)avT^%9#J3T!;gqlp8W~3+( zS+09@6bDG;%I*9L3UieOqtgQ&)CDtc6JDUv(V9mvf%&nMSNiT9s~Lpe`eOs)jk1!G z(a2K_j@jxUOa5m*l7<0%=pk)zI#N%!p$3d}q=fo{`{vcu+mPNa@5_eXfZFKd32 zHa#e9*kX9t)BSUx!em*!trewdLhi4uF6+na0JCi|;4HZyXY)d^{MM{|yj;7-()MrvtR~Qc^UK>%>n!2-){2BRi1V)AjO)E2Hl6 za+ON6CyJTs2V~1{B$*6Ee*Jb54v5Rc9Zs#G zaD7;CaKsltH~JD167W9m5u>1EVYSIfOZN~#t`6@}fPoX9M(}nQQAsA~2>NOcGa6CF zh7B7P$p%ZvINB;uc!~OutX4#4@l_M;05=uD5%c^`a_wwYT})QVXDIVSNfO<@fCx@c2(PwGd1KfSOpu1={mYWIbcfrbXIr&rC|d_#q3&=n`J*+wPIZ2bcRUx#vk zVTCp|WtIXEKU!+z4TUQRDvh}wS4F$|4sV6pySmE1eM=J@{8dJQKhk957Arp~Q|h$R zAq?(;3F_!ajvM#ZTZ!B6D}{xaQBkbC-n;Gqqh%gN<5of!QWCMZ^DMK76Y0=jG98&( zQxX-CR1 zEWdzJ-j3b&b`wBiq6ICKQjXD+a%29;a8=3F)z`cHDC`>^p2UIL1o`}PSqSm+G|=K+ zAchx=CWI|G46)|BJInK%h1{3WOGr=!c6^d&F|#&R6KsSp=rDI?6tuTvSLjuBw;gwC zE<9y&ePR^k2i}^;KBDzoBz~NF;eqrUiuHhTE-4`_dwle5xK=8i}m4MqF!++r7q6?HEKK zr-3vVbb>5CtF(-U$u-;VMwHd-!Cz4yNTs$%gJ^&)L=DucKW6&_lA1MW(gkI458o;F zMURdM30{WbrIU28j|~qy#g6Z(bU#O~&r#2!phz(X>eAl9!9zu0_1#JK5o#m>7OA!6 z*(Uaovq_LsSI1E5f4^z;t4-=K2c)C92;)%bLLSmc3Zm_|Qgd?p5(=y2Q#Td}(QmWW z$S|Y*FD8_~`RtR9dEW{nF^dRkngrO9b^?PG)(65cQDYWH87BPcY9^HX+)b&*G{pTe zSeOIPNl;`z5yEMlm`Vc0M@O#0OdakXITg5=U-uG0naLKOd=~7EjZ1S!b9s5`I~}_1 zy?gsz`9`UT;C#o<5`GhF-|P21#yED3DuoXHg_4qdS$kgs8u0r@QEOQt$cW(tkhAl1 z&incKbI)e}QEQwLZ*C6rs~iD4ZoMj_R~+QDiuNGJN)+F>+J*sgjg9P215UQH!m{3wm&;1Mj(_4oUPu!|3ay zQCC-Qzp2nICxuMgircUn(BT`X_1&KDaOw2eqL!=~O}d%7z#0ec1_eEBN8m;}tMKcZ zZ{G+?HS_yts*Dk`K%K5+BLWkK?oI zXOIf{`gW$4i)Um{)bgr|fM$tewkFeH?B(iuq24Sx#30$CK@T$g$7Kg*XEpS`jJT(P z&rUx=Pr6dDJ||afQs526%K$`(we6U~t5Ru%RpAelcL>#HR#mmll%G35aFUybT!NQ96MQKin4lcbV>MIkN z{ZaGtMhTYilfG-xRugXJ;9x{T!V!IjR$p}`K(Y^?XTJuX%dA;`f?>UB5!}D$;=i)v zG{c+VbqPy~h)4zUrZSr&Bzv}4k=MksOoXOL<5ZCNganWM8TDb>&fp9Pq(VMbz~-Te zC+GGb!*{CbP~mvj0gP$@zu3TiP$(R>_WUt0P^6sl>|Kf`@Dcv`r*g&G7K8sO8q`^{ z_4e(>YNeeI4O}e5Z`dEQpd$ijQPgvjcQl2qU9X}zcUJi|^zQDi2r+!s!)q1T9BbbO zo;3{H*tK|I(TRExE#C433xmXjZl0_uv}o*#i&r6 z5V$@KsyEChE1AFeuq+M-!u-W}c$AT#{3EKyx!i;pHR<{Fb-wPlSrxC$G5ZGK92tK9 z=*$Fg{+d0v_$etV{jeMG{mqSR zQK87J?bm5XK|w(+mH==mAt9k48~&CpmXHX#+JJNB8KZ94(g#E1?Wf*othtULvi;#* zdy{7OXHbr4x0+&e4ORBb!aSH7?)j`4*@cZwv4lBF$y2&(Yn$qnbuXTbvx2%hj??x{ zYc_FDYbu9+-I1NQeri6fQ@D2%wTPCM{$n@nV!`nqxaC^<`U4-g(->pf!FcYJya)RkcVec|ExDg4%Xtmw@#Dkdf-c~$-(4rY>D(30;?(UN_PNov#~qD$cfS!H161-#hxL!*3d#j5E^uJA=%h?YS0->(Rue53rzan ziy9hAu&b)XOIx$kF31cieKRUOT&;j^ zhxX|SkGZh_uspq>8=3*A;vS%VABij#tUp1e+DlJl$c{g1#`?4K48mEYzKxwD;lszr_bx6V&C~vckI!=zRn@|2 z$BDT)^oCYaLeozFd5S@DlT?vu6qPHvAp{IDVQy7F_w>IXXG<`5kfgr}tISXWYHl*VT2dqrmsg zrK!|qXF)|q7KC(1W4RcopX?nP%NTPY({=w!D>?mVdEYQL(rYg5U1!C<(xxAzLTi}f_y`2pD7A2slim;D*j z8!z`d5Ph+_UC$O6gZZUz%uzrDZ3R-QDzcwI`&GXAFkJnhlA*FiNK2dlTOk(LQ14*Q z?(t706oVm+oHnd|tWMyz1NlrkWNJH6v#UFFEoW(==?Pk89AN16FW27>N%+#}SRThT zyZ}25Sr-@H;AT7`Zc8U^_{Cf{wX1GGst- zdzY3Tq*6oSn@D5n3fZ70w7K!o+k6f`=7KROC*}{k-vfFqumf6R;;2;oXd9$2K?nee z6=JYsO_qI=nbXJT77qk%*Pb5l^TLvnl0MB6`N*;T zZ@0*}WbC~K7Z(>#da))?*AsXQl@M}1e!tnu>=5<&%LIHnKFc4hU{z$^0U@i8069E~ zs;ieJKls-ig>^(qN=?sz@?Dg6ne~c7EE29ERBsCLg`tklpaP{wyt+0>)1i(MH`uaJs*H8CqHrDkW3nM9YUWD&tx$G>-lnlg0wB zW|=M~EiEl>biwbD>)+bX|M|q=M7t$YfHU#`v6V?ET)tyWaK4 zC+Eeguj)ShhG;uAQw~yeGBrJYvUB827DO*g`oZSn*liv$W^XcMuxjG_p}v+;d6cZV zI4#%`An)w#1XFxyN{U06Qb#8WAPVD-RR0*|!9o46!Iq(g%^U)1>fm}17c5A}Xg8;O z|9DQ2i3$8v$VqyShK9yvn(a#tUnh;6)713zddtoy-&gs7XIg-9y#Ljo9SZ$N1DSIl zBINL)A zXncVeNbY{u1<@9NecI$ttVQJT?HwJ##VKn{yG*!e+uGV>3{6dsv{LdhH1b2wFE0<% zb93vwMn9(Xb+orr|9v?B`OQkX*4=`+yrP0UptR(wCki9|U)uKX*#?0q{P|?$=DV}C zWjQantw8%Lb#ODVY-%S-!q2s;kzx+F+4}JfLmH?upND|Md7r$82PD?e;OH@tT$SS9 z&x+?BH?@NdF){4Cz=g85mW==POXGd5b3tCCm4olF#yebiUBs?;QoJP*i7^2#Z7JbCDIzPm*JIh)76CfOx+xuZY5H z)zZg#9iRM4X@+INbJ}oSf919vdQ0c_MBa#d;MDY|cSk(A(5k(!q0Zmq|$H!t#kv z&OMj)%VO=KuCUc_0Ez4&e)b5tiOE_Jh-_%+(O)8#Sup2aEzNIyea4b`FLZx*H&*_YRTWKk8!10YWdTZ>!yDI{Qv`H~} z6#=>7ZG~Wgv9+~z805u%jY$i&=j~asMp;=%`*T7Ju3WAPkP|N$_;6_-X@F(0pq6Pg zyl`}dxilltwqX5ueYjBcr_tp+Ka zWHzZ~pBe1L5wQft{dDp3 zLqoob_V!>rD`s(gzfr{03DDR~b&BpTeiwIP0;%N-big?vzD+Nf2qS|u3e;^uL}t-B8vCi#F;`_V%lfEj5wuOPxs9vyqzAp)mGM9h8e>pr^Tlol#%7sJV zqzb8Q9m2+we^OOPcD8R6Yf79~}{7f0$YET5QIM0A6=$4tnOz5u@RjFntHE1+eXmQfsW;qU#O31G5JVBCj6%oAFDQ5*2K~iLj%@)rYFl;n zSQ1_0OV$09G)CI))t%_VP#kFpF$56-85s>t86SC#g)naM1w-1)(n*J<#YM*qu!CAM(d-Bzf$XX#J@&}l gp#6Ux76&eaPY8Qy-B=+87Iz`96g6H{%bQ314>x)n4FCWD diff --git a/data/gui/soccer_ball_red.png b/data/gui/soccer_ball_red.png index c490d53f5cef9c3da9a58bfac0cd533bf1c61089..377ba5ff6f759c34c2aae07923d5c6bcedc285c1 100644 GIT binary patch literal 16789 zcmXtA1yEaEw+-50MM`mPai_Su1#5#8cY?cna4l9`f(A-)cWIG8aknCc7AaobU%r3l zWs=-UCUft}o^$qId#!bD?0XF*JZuVV004leqAU+a9-;refSAbleo6CDe?z1bG*Otp&zG~{^e2qv(SB~6~CVErI;+t~J zDml30_QB5=W7@p-T15IoV2{n{{J|8Cugpj~UFJQpIvoQ82}8vcLxr|Z>Q~TLjd_uB zjS%n79FYxLhl%E06Pu%1Z5`!Pp&^=p%khVx-P!^VEI9GA$$oBaNDvA=HjogT{yXK< zegOlN;5$ao3q2N+`13#Re$H-gZu{*)E4xtxKB54cp34R5?{3bDF%EwP0YQ9tRCM zIk|}YM$mP&jf`MzDuosR zYFe{n34rk<%a8*-KflnON)hg0R!4S ze%-Z{{!dx;N63Zv$(7h~+xE1Jhleqb#0kLF*L{Y5|EL%5@W9WETd}qTvk3zTB|=5G z-DFmUmZ0NoxIc9w(EwDBlAd&u+c zWXMy<*ImQ6h8JC^|NToLlwLHW){0jRV-qPr5U>=ilH@}bu}uff_Rj4M+`<|PEI3(i z!Q1e1<2M^S3G5u#x)jx%x6gBI7Oo_Y?2(L$jwYv|_)eC55Gt!M?Otv2tX49ASF-BT z`AYW1bJF2s?x0v?Kp*~AJ&*4xWLT77wn>EC+y-Fy2RM;$+OKANgQ16R({#rM}p1Z zSe7_j!Q7@?b?3h)yO*o29-}wH^S}KPao|t5aLKDJ?%*>KAMOG^02M5+i|B^o^k00( zdqwOBFp&cdXfxW>FwkX!@Sa zlj!Fg43m=Z#;qU@aX+PdHTVYgG@|bVo5TyaZh9K6QiW8&$eIsr=NG{?puYyd^+u=m z4hue40EFLhk6;L(p|Qp`KaoP+sh-Y$ifAm@cez=F#hvgOaWz&Os5?b)mF=@VM z^uJYvkPVdW6O-n9B!Wt#R0}hCQ&EkV29T1r+XTZ}kCtS5cYle#40wUbav$uXc>NyD z&%VgRWBwKTrFU+KWKfRG#_nrN%L_k~Yd3J&D7l$cA0&l(@t$uTJg$6x7YR3(Z^Y(l^9j=o5ik;?{ zNkzZ{|00wdh@61yT>(PjJ(|m29w<|mx1W`7k10=jd;NHwdI?1)cLq_8gO*35-XPj> z=zB<&-?~Pnb$3M`uo3M8;IiLTq8&L&AsSi^~2U|GuWAGY?EY85qY%FpzX6IccG?$5KkjmeTVkVSDs)3dv&NhI2VR8+4SFyz7i>Kkot zrIM(AE&MWH=Ml^>*}xb=hV}Actf87@5zt?z&eO1YZzRS)fLxn%TUm<4jIm#u$0j}s z$f^Nvwtju=`B%=B`1baq3dKwz=K15d)xz0r^?vV5cY0g4K`<2`t-{=vu(|8dCedBj zAj_6s_k{8JdDrzB;&x!UvCCsu!$e3lhlUxNB1`YrPIKos{*D~t!a&$S6PAKbP5`85 z@uzWF>&(>57md`8QzY|Nkw|I%iN|0HJj;wWz5P+p$x`+^x%G_Ruw2aKZsr+>eqBhH zWpmmbHbqAOB@XkZ(uyxmv)ds{v!Dn1R+;Rkq`#hMS{=hgX_S%XK%XH4_4yX{jmyay zUw`cTvT%PTaAaM$T07}DDuF+hh_hUFQF`0h<1T82{AJx98slRXcAq=;EBRr)R?%!=PjXDQCk#l4{b(Roh(pac5GX5dYQ~^a?^UWoPR45v zj?>6?5e1UVj8o1B~=5-n*}@ za~0^X+de}k8cXGpY-yEM$PnWEVx!%&vt0-OMp-+!f_i#?`FU4KyWl{DsKq_^Ot~^_ zGRH96c6V|&j&7bd?qt&_-Nt&y#zVpAeU1b?)aV=tUP2X-1<%fL#02cmT9U+&six-a z-pMWow=YV-)uFkW835r1{^0VwDg`GNTK9e17>TMWKmpZ#E6o?z@o>Raux2vRh|}Uv zQzd|YDY&Y8YLrQ-q@<6Fs$0T>W%ey<_YYYv#wP85AjPXbu#*AMcAa4uOiBObAOoEA zqB0Q?AIqlObSW!~Nt3nfW(jqvsya>4D)-%(F8SHsAiJ^0a({JXX=R08bQwT}7;yVJ zxMxB~AnV$XC*->L9aUDS5LI`}7q3z9-9y84mzT`UsPCA;g(n#YVA0KY+dA_sNlQz* z_@&3>sB&iBAGT9gV-c@!p3{J~-33D_w#b|6xg$bUC19}qwKGla;_c&SW|5leD^p?b zgrq#tL7@XLMMb6TtgOP?TKvLodcEi6b?2wXdnP6(UnAC?Fgm9_f2vo5ZspM{-2(k% zu(4w=lL>*btGR^{5c+6_@A4Aav!IaD+eEu4^URcNnp}@hRIc5`H=Bc}C&iA?PM7vS z9|^a7B3rbz986d#!0t86zl93?;|6)uCeOr^o}PlSBT*A4W>-ZePNX`eTpst!3m~hr zn&*ksVl%s0EW~7IkJaSfr1n%R7GgNKh9Q*Ud7`Z|~8CI;o z>u++=J)ZR9U1nIPa6@Om+%SArQLgyn?H^cX0qtXk%uF*Laf5H>?FC+6mI>J?1bPSP zV*jo_S#FHJcM!;N%loMY-04qVf&jY}f@}Za>4c^a?>D@5Z1`5Ry8Ghl_zz#X=4_m0 zj);BXM*~r2xqU#vf9NNWKCp`p@H6P3np+21&ahGj3@#=)R8vo@!ExN&TyG)@kx=*k ziGp|iKne%_oX@J39qvjM9V-#g=HkXX&JQw^1TI*gV(dST8_9hh=2-`re{3J?u}{~O zI>!Yg;ss@76hT-ycmYhQAtTVzNs`Y>q?w zI4HNL$YPfNoSnJ#@JMM^iPlH{VJXfh{N_MrP1c_MXB`V@*X1Qw%qlTD8j{9D?8-f^4CwC`z--R^N z4PUvR7pysFC@Cst;mFY#=lIAO?*VQE3Z+*x)p%moK{=kh9*hgGO#Fc z0`+VKXD_ZOSd$waRkGfYnh`i-0esfoM>}74EmWx<435=S6W4?D@a1Tx687dx&dD5Y zCGHC|sW<&-`uqC>&7Esz4V8)1_zd4;P*y}537D&7i=RldrC$U~3%{)VFmT^(_2X@ly@rBWKiSmdt^@-F@4-_^ODs@ZAj zl@W${)r+@h0mr{6+1ZW!{-po(oK;s#N;r+zlpHbU<5Py)DiOw4lQi?F$?oK)0I{S( zLK?**IT^y=us&mxy~83ZK~10l0F{$zKqHx?q?hNhTsxFro5C6&xhG%kFJE>YETM^% zrs3i<51r=Y;B*xl3>IzY*2)vURIxSKOLc>b`o*bp%3*U@8wZScIG3{|iizOU`#PG3 zUQ>$>oWdl!yq}-=e>xehyqL`2u0%pOOH0c`&P+Rb!fKY{zxTwK0Tf;fmbaJx#l`n@ zst6(2zo@PP5oSI_^vGq##rz*c=tMVXkDbf6t{vs8y|N;dOA7`GaP-=7qU(>DyFUV2`lCVCPA1!Dvl)MrBP0 zHobf%al&>MwU~EZfP1Qxk^Zl%iV7=+MsO7-$|sh;R#*Y5DA0msTvH(oXp(VAH);Qi z^gZ0l2{ajbaQE9#mm2ROu_fnZ^DNGZfY^)bw%Dt7OaSm3qPz^KO%x|MgZ-FyF4Nbp z8=nE0f}@9O$9WUbRcoBKYKniW4S75;3h2kf#mBET?|Re%xoxWpoekdz_xc=?aUSW+ zm&DAE`(dAmi;FD#r~r6)#UHeYu2|uzADZ9rt!tps&EdwD{+^znZ+@!9K)t6G%)q4d z170;#BZ~Am-b8E~HdD}wc*R8>a`Eu)M!JpNdgq=W2|9`WXqo0lRP&FzQ8AA#s(l9k z3)r~93_1J#*~sVd64}jhL6knsy#jtTa>ICvHyNmrw24i66pFdP!T zsi+gcMK&gR@~w7e5foHPe=lns$lmgFa&acNbr^MIy4kpl@{t@p8&oT%+2#FV*;2*9 z33LmF6%+gv=II;7#B8hSE#Q?>Da*dL&FM=aAT|;r7ys)QTLgh2aHR>~=mZo&I!aWh zCxvn!9?Ol{|6?pkd4i3ye zhAE(ea^tWeE?aRO{f{|!M)_7y2ihv`;%RtnvS@yCtC4u0ukR1AYFJ7LI6@s zXi7?okD~0M204Ux?dNb+tIifljXYtwVKq#skv#7ODkZKVJzChGKh_Wk1mHWeNq4f` z93Qv)i)B#0s1N!4^!O;ey-kNcdnAa&J5<&4H$M+K$_xCWBp3f+%(E?OR~i{tyQ_yt zh4&OBh=OBf?2J?ytwjOzb$=_7xb04;N9JuD84XRDLACegg9)Inb@~4AC><0j@dE!} zQ8evN>-9$(hKBfiDbOvU z>UV6`vTHMGs!5b}$%88r{2+|#JKN&aWo=P>?l^?=K0i#Oh`1BP=uHy;Q&2o_2QN{8 zLR`1@{4)Kuvy@MwH0UaAl zn2=Z|YJj4EFz@p^6CjXvs*40OP1X=|@%;AdvapRADJqih#rd`m`OKWFPE$L7vztv0 zv*RZ+=0-=d0s(BVbxUGO*w>uI#l`a|;N1YT-p)0W?v~t3WZHBM^h)m)_KLecTvd{Y zBQhMs#!cxeCuO~4+U&iyzYoOP6h8T|2^zP0dxbY!R6;`K`Il6<97jqmNzg0EFYOb- z&d~-3Z~`VVIx|Bg)f6PQ0Y|)GN&QzDD;t^$5oaK(E+w|jryR9%G&f`2hIYYTY}J{I?i zOQ~QDcHPw=S#e@p5&m=`W>;}d(t-_@uAmjP7$6H$bfi#8rii2fVhgX@!_|aR`=;p^ zP1KI(0o`N;7~lT=dpC=O|JD|7$f=6It?c@#5o5QlXnZhuU=K2#RW*|E?6j(Q$JeY@ zMNFSj^WsiCDH9)$SL7Y4>?v|!y|+u2`Le`Bm1~fv$Qb{7vVcy!;)B{=Ski_M?c1Ko zgQ1}>2LYm>4@qwlkxWWq?a8I7--s%v%kAA_=DBY-CZKi0ebm7MvCK>-5Xj$3M6ef~ zLV?bt_&&b4lt7DbP5e-Crq3G(PwQMdxt!uV&+oBpzQE@J^jca<)tVOxB-ptACJ3Ge z*xOGiw$hYkl<$Fsbg3(fxO)+F+H798xRsIMu0Y$E){~XN4asx0#rq||-ryb%2DAu? z-&$NYCJJEUDuy6$;`l%h>qwXCs=7|?Es20D9$l7;qPfXbwVq~PKq-NXkdPXpjy3&) zVO*~S^DqZ$S9v_iFTW7d8k!(?#r;*&h zRr&Y=FXmVQH3uiVL7Ao#m-F^thXUd2LB8()u95k5j!-LS0YFEz1+f#rO@8@@h%NR7 z*^roLL&&U5^6F?K+qd8wj%AjU*RKG!t*d!bAuvNsOyAi7d9X7ldM5}zf%7NkvBt|o z(~_2K$dHbQ4pi{d;5t%T0x@=%->!ya&)@?VRP4+!fcx^?jHh2N#&p|XBh zApx)?m-a=bJq*Jn0u__O+q3~ohXQQr*n{&r$W#=Yza{MOqN$HN6%ATrBu$KPN!=u` zG+|AD`Ije?SJaeMK@~*e^-WgQ>!-Zfal!gOIEbyP$ew zH0-u)TCZ1HxUn%J-dVo}%|$k-HQ6?Ws;C2}{%>itNT=RZ1oSeT!80#UE0^gud8tub zF00p|kb-XK%4m}qq5E*U!f9Kco%@I1$7?qoG$B)7DlUU}H0#B~@tNC`Q?TUab7!BWamnHpPBSTXr0lyHJt>b2j@aw|zK@4N#zb@x zBmBFX-(4%TbFhft#6B)=XCX0?9&4Ear=*C)c;U%;IFfxmpCTyf*dVgEJ+p_lp84qtKHO4+#V~lPb18fil%tC4jm|sH4;gseYgeP!pA#e6(3ephZtk2FGOC}Qjz}}_3&_z zc7#nnnEqG-Kx;#7hJ%yRqU&DylAksU0LZ8*J3DI9m_04CXt*y1so=J%BdM?{dfm9m zi3zZqaZiY2g5;ug=7Pdo7$VUk1xe?0`TJ%yR+Avdg{+BHiqjP7W+qIPM2B5Zt1V93 z|3C`8`DS|foN}wIJ8kNrbO^xVKK%u1s8^oKORP<=?d?E6XRe^~F8Qg*xH}U7FbrCZ z**f+CrEL5{#bp9Rx^ynS(ce`6pa{&dH_Z;J@B|E9CnagQQ+cO>Vqe!#_Us8evXhWs z2Qgt|T@3hhZ zK+xYj<@A76{Y*`-H`fRelRce>Gwy^zg%Xmq{)saM(wbJrT!hy zP3-3^Df#L56iABZ80%dDFnj9fv1)riY#sR0*aMmBeuPKAl)xYXFc?{*iZfliX!qF@ zPcJACDpCH2O}gA16XFiu{qE3~NYb;z{{~R@%1lHs_}kC9kzI?+vM!sjxT?ctovb1% zbmN&R@iYV}@>+}Ip3MWrRlMvKiK(OGwtr{9bFD4=OYcPS(LDj_^N(LXNknh}jhyJ% z;cc8R08#oa#&tR$ka=1D!@x1v&KN7Xh!kZF5EI$<(>q^HZh#?@hDgt`wcW-%^n{$U zEH~P@dkVJyFyKgV+PXWzyV-$+ zflP~2i-$gIO^eG3tx3FU`lRR!ohC#=DH`Q|*|1!_9iEa*W3!A_3!a=HbFu~-v%*&$ zi<1kV)BNdOUn~LM+Yj^Sw1>V$M>gbDlv_`KtWBmC5p6IJpMmQ|$csxkQ4%_I(X=Yc zpB$9bY;@WDBVL4qZ@{cv+?lVK3^O{3!X&Hj8|k3V`339DFVKw<5XWQzumv?i*7n#V zdSR)Rbb#E3!M=WUilSoco4*0ntImSU`W&dSX=~`+Qyfcdg+Gha#^4QUJ(bK+ezfZ4LGRG9h^RWtBokTIfY!|Ss-1O)%JSWM8Si=Vc9 zzW0TvGh(!b6(%5*f}WfL8Do!*TEGBIy*}cfY=wO}!GMOcfL2IFFGEGFnsu}3n4`%#87j1!??@wSHVqs_KjeTB!5S7w9?<-05g%$K7%u+p)*s;+J2hrhwb(dDTb zvRI+DUcJQm($V9A9@J>4*1TdmQi|PP6eR()o{_1Ghm9vT0cvF_Iz$})){x`xCRWET!2yg|rgsD$IBv~4`C7>r()gbG>mAs~%uD7wc zD4`rY)a<<-8AJ8a44KLul?Sa>sAZCXYh>YMSDNy_GuG{hJ=D>hy@LJ09e)wJ;g|N` zL7jKYSG|Zc2-dSPvbGW7WJa zeVd;6b`871sbudSYeqq2WnURNDR>u~DK;)-JFi#G4pRyI1F%soe=i?FsF8RyYFXZi z7@OXAuuuA9lLwzwbg$64gHZ0zsg0zx)UOiCR+elsITFN~Ax?h!=p8IXoHdjC--tiz zucWeWiPW-i~QTBkd3Eix{~6d+KWvG@#f- z15D_CUpeo4)&~Hwe%&b{yIT{ae*v0uL!$)6Kg+4A9&)6!h`t;b-e~}{c4yh>Z|N7T zn~)fkuYOB*O#c+#vh(T8;-b3;U-tc_I7xr<2Z>*v=;%W;UU^na89hFN4-RB`r0l*e zYw{&mf%Hqd9PSeo-1Q^M#{Q1AR$#KI2=ie)G_*EZ2Cw7}&W@+XQEIl5)#!to&Km2t z7e<}qBZ}dVK|v}Brb5cgu=)NA(M~0st3ilul=YRd#u8}w1U=8 z-R5&DivO0@Q5GI?`yDBZGNWDVor~8Nx!E(dc5Du41XaO5xVY(Y<#NWE7#IKi?UB6u z)M}2#7yWJ(Z3=+&MEsa`F_ZfF<-{jJ1pX!KLsyG!xnb3A*mwRM@^vTAp3C?9ryfBe zFTVJjysJ%OqL+WSK)V2?>(1!$R*U{+jN6DisGVSl$Vv9rwJu#;j>ygW%1o~}(@dZ! zwflO+F)D!{S(jU2w3|^+!Qz~(omssitga+Y*n1}@AJ6&t*fPLb(XX;$mIHal)Q%Lz zs<;+9c$E`3C}Q7jWxKpjUSX1nZtJ;H4Pj%@RLp}vICl{nHi=yYrGuhfSJ~)-G~lA- z8XYAu#-rq3sg!RP7RRDu(se_C-}uIvkhr2Jz%gO*C4M8ZLLNRUWYvkJBu3{^KQEHU zYH-ZC=cV<0G&u(2f@QEvIv)m-T-^xr@%;mn;N4v6V4M4te?>Asa+zY)IEfptNvirSi18J)=+G#E+0m8}7f}(;7D(~-zP7MW>s!}ON;Vc%cxcUod zPP!L&VB&fT1r-y_FKt{xFL4P}+gnmQNN2@XbCHsk0SE=Ki8F2ct?*Mr*~q4safueC zEw=ph7gB$s$hogVn>-1NXCeKz7n^FIy#>w9Fo=yagC|QSy+l@>J>Xn^y0Y&-r>06! z2;R?Re6!Osu(MiYwq{tnyANxKC0QaZiQyg_KdV{m)G8G2`xY9?04*W@#8OO%Xeuow zMLegX(J)8I62V6ZC3pOPA|m!HaF8UA3OglLK$0;DjnybKcz!~(xf`w{}lMF zadgQ?6Xq8@Dm);{6o5D8VL-%j$>J!jzbL~u3f)%g9RE+^^|oRV5WP3b%(G=-XLGNt1^EcL}k>IDS#i^Ee5`YlqT(l`#j z6#7*dQ28)s9tLDNEYpM?nJ>{>G$wc*>#oUbojZQtq?PoFTTDn`*x-1RLiAC;J?$cR z2-;Fpz(oo9su4#9Cp!-cMEn|jd>ljS#7zdutetGNv*(8n%g?U8`pPH-_JAciy~H=) z)!wqhN1HE0M7{g?+b}S#Im@ASwbn+WmE|jA?0*fu#~rvAFCY3RDzoZVpFMc{E=_r`7+P4@2x}4q+J!@-;;OG zl&hmqb-n%u<83d#lcEKHDqNP8klil2Oqe5UtQ!6zX_H*VvJ8V=?HH8q^=%aIf}e3H z1DvIu>$_XqSMV=$q!))Gn2|iQFhcp2sy=49ec21(A*}k{NqVMcos8L>il{b~@Ur8- zk$t;$9!Z$vD;0x#_xx&AM(Cy5K`5Q^B#+0~HDO=;-Mt975}sP_3_1?@f~sDQPb2EN zQdJ6YrP_5sE#3cxiLS~-6#ev4S`mRD37_?QaKy|C@|3n9b9n=0RaSH1FS1JA+YarK zL^5PH2Gxnq%=zTI!BWP1%7f;QE@^|yJ| z=^uhmAS}ysc#jlmSYxbc-omvFWEaEFqK+FscKnO3Ph)nXlx7_Slg-3^Rw+b#bUE<0 z;}u%%*GjP`574gw2vZQ#_@ zYNT-l2*}ByVF;aEp=`kV{dSozf*{YSH5RVci=O)FI(I9DOY!gXlEoK1R7zpvt(=Kc zttgeCn$aF`Y}kSO$(BW?mov+PgqcdXxlV}5Izwq8B{~Gcx7uL(_KJ@#MrW&%@?M&4 zaN6LLl>ZKSk4s?Q8k{IOt;V!0Q)|ryC2CAU`z3HefS5j;K8?ZD^5<_oM!N^EJym2T z7jVfdR1zE#a6vv%A-l(i2?;Q~8BW*<~XytgEPo^(g1NM6; z6vafG2Ozsi+3mv1j*l*Jtv{wWsm_n2>tyMv%Uq-a@D2D3^+*AM8@;OfqJ4lO8U%+t zAvm0FetC+sVL#?<$v}fSun;Yw=0+NyAnP2b!@Raa8!7icZ@nqMJX|*3)f0>hD~0s& zXoBp2#I+tQ`D=l4z(jl74Qy{w4cydLNw(Rtt8T-YHF!)|PS-?P(PG!Hhj}){%Ln%^ z8)9?aUg7w!tRP1-B5cn}^+#<0UJ*-!YoSF7C@U+25zBJepvi(*Ct3`Y3#|_xtDefy zQ0ENJU?A*OI=OgD)rQj5J!QNNH?oVzf$t>Ek76+I-VG z9I1Q8h9Fc{J7pDh5ONVlnbeM2JxT9(>=XMXlfh^u{Zcd1{$p*M|8cXYlTCTNE%laz zq1fvBhtgS^U7^a1T$9fd^*L|QT(l#5=xT_NH;Odlg?hi|eMrk1)0_%UwzdDzsVe19 zbo(*MqxIz$S^ugd8NJ0c2CX8b%yDi$oX17{TGsXJ{MNHr2&`8$AOk73ySJLIJKEHBW1sjVW`Z;ow%-RGm#A#G-2NwSB z;NKRWe>8Ui1ai1+=jeZrAb6Go-P((tnfs5m)0rg^$k)WPqt}rC;FM?PR6~4v_s5`2 z1|6zl41({X)m`|A4ygTL@KAn}VG|B51OIHSOJ<741x64wZDmD&)D`aPQXBMYZ!m(G zw&!W{Ibf>L&)^p3e*t}xuEH$}uJI&Jg$$?>oc?;n9FxAKEGz8G;%$iytY}ZT%Q7%eMnYMU zWpLeH1SQ@X2hkJ!5@_Zm=*HeZgOnnrlN$~iC=j^&C&1rRCp%7K7=3edb3blwZqWJ; z4x_{6uQ?;>!xCTr1sV@-3+JPL5KR}IG0KOE+&YSvkuD_O(9>7xxzD8U z1b%lm3Ro}FXU#_1MT4p3Ddx^!B;89QrIOXaf+IMQ!XD8q<`UnL;Io5wMzpRj*`@e; zi<4pblL;_NBFWrX)TY4an?kv)XOc+MwLYCR!!np2oe>X^W+9ST5kdcwp+${~8_W`T zya_=m+~1asS**adr)MRjJ{6~MQ&S+dCM#IF2rLPpe!HW-{Us4UpQiF{K+OU|kxIx@ zU~JhtcY7R?gea{vA8%ZlWS6%Ai6YKz0p+E?D-^?Rts{jK3HzYC)4RfHNM7dvOfup_ z(9_c(%kkMxZ$q3{Mx|1sNau%j#@=LDA5{GHe)07oqdr05+)B`b$W#R%huF(_^O&iq zqik<6omV=GkH7IHZ_GMBuKqzab9O#HQB+cTRs-_dssiA|$Pj$gWvd!;GiWjg^;vMr zs)w^HtG1pWqITABFSLY~q+;ce7RMM6pKbno=O^7%k;yPj^R~G(>^I+4q%3{C%YWWH zFUg_O5e`$GiqpIL`ckU3cQf7@qrQqdwnJ9g(RwylL>)3_@ekr_1vL(4to?(J%wYh`dW z(;b=(3Bf2ET$Ap#S7C1XTg?G2pHtjhw1+WdxY6FXe&hmbx8{b6l=PHZRYGlUM9uCNIyv55B`>%ro6U1KhJ!r!FYxDfbMg|4ek;sU}q zQXZ5e#jt_|k&vf{{#UPFnd*Sn?1c*5)?l{$ZAHBW>B2+EuGlJWA1eoIh+Uf5)5<-? z^Eha)1xGeL!ezWQFu|m?Jke0|HQ5ib1zpRlP9!td(Z1W)2T)a2ja|W!^Uegt{+^li~*2rwVLlMyxSdO%bpZ@BpJW2b)^ z;j$o_diZs5RZ|^2bnU!xu_>u?Z|Yo4tiA|0YnqMjsDfMt(2ZdFG4ZK`A$tF4ijy;e zqK_;(2j*sy6KJL`ImJtkRNVG%>mG`Fpf{6+TsGEn&jRh}`NBI&;)AF2K z{e1nh74qYZoBl2|T3aj=@rt=IgbwnQaxzAL{D@_>ONp|44{5D_ftuSuM30Ytm9PV< zxagm-2`|)IDBBpBcXpxRB+fRs(;F*8Lp7(h4DEw7iAX-S78o*24||voXY0nh2vYp# z>jCWdO{f3);~RK?h3{BqjCA&cL9gQn@e5xhno3O$t+JIq87+U>lG1_mLOqoTC2 zeFLDJ^FO-%-(=n{iTh+8b{28XbbC6UD}#X@GRVMd`J-R<r{pHUV%jE43I4+zJBO zIYFB8ukhm0%+LS;HFb3f)Q3+q)xXhJJ>{J=+sE+c*;a8dkOX-xC-=Et^$^ky=}1sV z5M_4^;7B*?_WYtVd`q{fy^|ZhY+|34YJCThHdE!gr^?-jbOzxsdbv?Wi#P$!vP$apHV`4qNdON6w38W2oIK+Dew%y?$7_;vG+_qJ>GXlCAJz=3upWbIuMLf zI0zP9nEL+x`#rOwq6s0J*X$LzV$}95!c4P%SVIEWp9v^Ti;jCE*%_yR6cEHyv~Nhf z3I}I-9qCial?~m-T6Gs0z5+V3tX>2(G^OO)QfgQ5IZjKsZo^? z(l%s87Ry;OZy3G{-ypP)s&&2GrqRk&>;PkFwPBN$7&~t<%B;nvjQ$W8*oTetIrjBb z_?ahzjJMtj-#8?dINnlHLSp=Mj|J&JFrJ-kF{p+^8r%2R@pI7ueeE8*y21N74o?rR zjkiy!%BVt#1i1SmP=}QnA7sob297OK6!O1I(4r%~$4J&LvA+(s(X-isgc|3%dA*si zNiND0I<7o9Q2Gvc-#aE4#0cJ5P=ukJhP(a_u-e)7M*EOgB4fcK1ze z%cMW-Rs2c#qLHY{q#1z@G|viN<=UNb@DJhccII17{9El~L_B#XcH9L@ohld+SnUY1hkwrAYNrF2sj2HjHNID|v4PdLk-Srx_1M&4>o^i4Dk^HJ)5qoGqz_dzr1949dd+9!ML^G{Xp;a|q1}L&6oqQ+ zj3xT(5h34m`@3l4Ot%f2f%CoVu7R7`dkOlsG6VIVnF>2Tk|)L0^TgEz2EamFb_wpwn^^WjwOA(RkK4D&{b7Ej_uE}ITz*0WGj-O7Ci#Pa4CWcZ3; ztV7+8h;DLyIIORyhak*aBB%!ZRjfy*yk@#8_qiHsCI}Q>uqD`^xQM*9Tvq5LNgBX!R0FWYj{DW+>*|@lw3d z$pWd#es=bop2>j$bZOm_Q95AHNl`S-k4KgZ$bJ|AaGHXk?REI+C6!+mkO$Uw7k{u#suC1*CHg8+iCgiDJ!XzHLGWNF|@UyqB1@XSCklq4AV9Tlwawi6?`@ z5LY_(bmpJ$Av#0GHYhbQ;a+NjI2T85y&{zTJx=)W6ipj^Q-o&LCg*7k`#eW(vbeE<5hjikB(XBQ)MzKcvqW!sPkuN(^GR4k z(qq=%O%SLzWr=+_QrkHv%-GW zsl-o9O>;6WTy&M#38Wv~j6p-p%;ZLXPR%*;qZ~U+Ix2=K=*v{Hu+YT=in13J26Oek zdkeq5XuM<5#je=KpdWevuLUG2M7s)|1#f9r4wfSwVtI)fIpPY5(>{wMpP4u6kKvnHD52ph)G*tc2OMHuIHqTM}22=N-m&?3h?v&PiEmVf74 zFAh|~#PKbc&V`XOXVwYdo?reM=FS}m&UpTP-G(6v#MJ%J3TLF5kF6WNL+xBU@G3&| z9mpQaibW&0|2@Xez2d2&?>Zwk^qpMKh?pAF3h%l45hSG3QeYh3ydA=2W#j!S8?T_i=U=e&{%%VYZl7ki1c zr%g1A{W&7V2{S=l*yC#guRgN<=OP~BZDz3$NURv-JrXX<#{Ev$GM82)elnnIQ(0Ll z|Dh@BOcwYWadxt3-)uT=at@wm!~paKaCcU@EPK)Fr~Gs8Y|Vw$A@}Ae+uZY@ZVYTC z9f-;|I#tr?O;R@{@k*0li6f=G!}(X*R&uR&VM^fw$s?}kEj4uRbQDN{G{fVCC3O2^_55Y3m~qvvLm!C z&G<^r5R|xZF!G*d*!oFV0=;pZr-u$bGaYo;n5w&z{Z&_3Onc-ozka@7VW*rMHuPvr z1ju?AHib!!Z_N;e3UYYebNIoo<8&*%9C5~jWiF}!eN&T0Uh^TjiN&;}bTj-8m;~*s zO;5ycvT1tCqFu7L8BUufQm!o?bY}EO+)y%n-w{|r7DO9GJZk>zivMYVw zYl;Yc;rk5R)0K+SuTkDPq`ndtUsOMM0$21I5M)_85@hL5AK|-E>L8hoJiF#L8_3R? zl6^BiCl^=nUwtbX&AS!=fT)v>jYLY4GRoz{CPmpQtxiLB{yWCTHNrQ%O=-o}#nG~- zQ$MiBg%yF{l22yKd+pbe8&7>rEE1L_<{;uKPDX?0EC()R0fPO_{HPr$py(o|-6RZ0 zN_5N&O!OmR=T?eOy*K;)(HqZ>t&jc}PH4~~d@A8nZ9Mpur{4<^Z<&uvS_*GR?86yL)h4wXnrnZxK9s-lBis20h;OW<+SIx6FBSvyor7|to*XStJ zQ*Cv6$^WWdy06!{rB;u+34>Nq04iN(fv%^TW#OA^pqQ}n%_`%m`I{ZGZmHB%KWd5l zzyIE)TCrT$fLT+}VM>E;;DGNQR9y)<`u%32UO^RcKWHrXz4k7iIDj+fbn}mi1U7M? zpp(^;_T73HB|{f^(tMmYg1b(S-QNXN5_gUF?CC+PfzKyK^f|^NXv&Q-dLroO2NY@M zP9Dl->RQqYG1`*>vYU3UMU(0$nT~s~b~R7MUuJ9EVm-X$7p(JM8jEK5L3uFQ-I&_B z(E{$V=v`%0tNmT%DvNW!y(ZbZ$u9ztM5&~i^YrKRIVvFP zX?DFEqqp`%=;PLn5+&&9H66G+TZJ13QB*WL9GVswX&g^p)=Q z5BBXG10lh?-PccNttH)DX~-?&b^Ag!-B)oL1Sbw`m&PcE@#%kG=*3ye!ohThEa?Oc z1=^JJv^qS$taDQw5~Sa8*$gQl^V5OeUQ93mAEGK77{+qjNwpZt&J#SHxm1w(;J7`T({;gGly3rRoI})`_ z`cJE$U6j-OXn1oToJaooa8u_NQ8V_REF$ z6`2Ux!#H#Cs2;8zgWD)17*goD_|=4u|2ncQo~2`30$-fx-l%&6UL4X27%tgBx@?)m z5$&vDq^$H;>|cu0m?KUB8-(8#fE%H&HDhL9{@>>4aC}q=va_=_|9+)>QpSCqzyAZ| W5`=5i9?t@X0)wZkpUXO@geCw^6>53_ literal 8845 zcmV;8B68h{P)|F_XRMpu&vt=gxzLABjB4viHxzBHwQ8%~ z#D!K{E#gwGZPhBSwb~y+76SxA5<&=B$eu}NN&fdcWH?Fg%suzcotcc5?|IHM$=o~1 zo%6lt+s}8{F*-)a=)agupX2XUj8U3ywN6j6T4!2JrrbEQIo8Zr;vi%3uE9a8hcPn) zSs5F|buWH&;#x1R_2BhDx7WL+)$QJn_xE7bVhkMvKy*@Rg2l2p(`Gw6(PGI@vs#lY zEBF%&bXCED*Wd+E_z>Kdb5Q@BOr|uWM&t zUvHPk(*aQIZ*#l<+U51WjobDf1Hkx@Bh_mCLb}cNKVWwGtb~N*#VIM7lQT1|tsNcg z)%JF_r>~Et+H7nA7(FvJmDw5qdlb7ePg26;Bb#?5~rY7bvo7q<~Gg*F7k%B=I2J4mv%6r7`{Q$!cYiitw z-0pTf(`x|a?Vh2bnqvS6f}qn+nrOHG6e7IhvYedkIIL(N^))oG2Ql&;4)%4tZ_UV% zGhGgZkbr>b4FGdvXD7?BT3i0p-Cb4fbgpy_4SjSB0CF;A*=!d;%-{6&tgNy#%E}T= zcDtDTfdTepRTXQ7QFnbwiD0~hBn%4+2o69cJ#^pzyLsY7_Etwn_w%i-d+YoAf86Hr zY@k1J3;<&Yj6Vxj{5=4tV}Y;#Ei1Y|A-e^?I3)02KBDK#){aq-3UGkRc%8^IEP;LE!1?VykOw*|#Q4 zV1=+5hDd>ceirxe@3dy`UUqqIF3TS_V0!?JHFb3jFchCZ z=yI*ZTdw~a060vh*$C47VO3#a=?Rl3I~d3XhW`d)emzz^f-Ou7!T)E!U$B6|PLQm0 zDG&^RK&}e-E56g6pMJ_N0bq;M(~n+zr?IjB;l{=q1f4GK9~#=iB1{$*VVV>hd+Yq< z>r zc?Cc!nEyHqq6=5dnUhQw{U5e%W8WEOya$sN3~d1bj>l*75C;Hh0fIUR#LqG&%dhjF z#dWf879$LOCv1WRc(nouzW0}AWyPir4b2A-&T98~UZk+X=SBtO*ledlLL#t|P z>LJ}$kTm$rOMqgB;~U2(CO-1>xpVWjwYIYFY}vy8f(fAHOsI&|#sL_Nr1y@}8hn0Z{^0zZu@ZzcC-vC`TcP+(py!l$4aJzP@GNp`imEUhnqLEC5O! zj(g6}%)I4Wxw#oX*uI_p0{R}}jfI1%0f6IRXcc5<69_Z|0r#kbKyD960OJ4Ni7;Sa zL|miXY9*#Cz;s$hMtrZ^y%bW?0ZH)oXAJ-*to+9=&&j#0$?ZL+KT0 z0)ZdvPI^56DF8sRfamw^<1X*t4j*QyHae=lnh`8oh}5>pVo?~A6XWA;6K%F46sAl8 z2sT750KojUw-ps#u)n|GL0@pZ=tD%=>Jd=1G{#|) zD{&v{4Onh&E`3Jnwke~9y4=F1rY4>X_z=w1LMI|v1b6w!vv@p;XF)=#`;UhX{StP; zEl~r2e7pU77i47IcKHb>q^V|Eu--gwXwh|mX4KA9#@Q%cwirW^n`c0g@Fyz&rq zvc04WG4>GKqf-(NxkxpfA)2#}8e1ThYWUoifqBn~a;&vHDxaLH93(KYc%~e1}6$j50u;LdijSZ!YVdt>Vq!S3 zCKVP6{Vb6J^$7B;MDhoQ8w-{y;2N&2s9^DU^(T#Ps;+MScURZ>s6~8#JOPk1Cn4cq zPb^wgOnUVnTemVqu~?+(t1$R(S-hASB$yf$2$c6wz2O@OTw$_V3Uo6h9V;j(2q*=f z+qH|`3kf)Y_ly#%m$@W}Rf2#^m@9Yf+PArJq(wD*f)q?qQ#tH^Y@54Q#Cfxk)Rf+uB)r7-$#sPVX*F@#emWGz#T_2qqr{-M&ikMh61mbRw3&VQY0)O_y8*CY}g`Jt98_~6voEd8l28Dcm>;`cJ?rx zWM=xwfohzeUpH%3CL1yNuRu>miY`XH|9guT@zv=YDilyc;Q3YVQTp}bGvYM^rCwJk zvXJ4)b$knp%;Rz?p6v&Pg_#9*`-Aw^9(Djg??3d@($cb#!IWl*f7*T(3Hmlx-H(s)MS=V5jLr?ND%BR@-E0`QGoGBg@q+h%MXSX0NA848UFFI2}MPgkrPAl z{z%ZJSozmM=R*$q7_VRyKwwY`=%+MD0rK5J5lML6Q(LQa8&O#Vui;DJQDxWwP#zom z*o{-C=CU#LFX*Rm)A?9stFZDp^930Jft=)OAmUO0#J9mc0Rwa_q1q}Nn`zdC1pvtK zUH;9SoEetHM89QO;Ut>eHK(7hWWHdCf+>+!1Lxuy%`9^CE7VrJ1=n*VB_L>peHByl zVqz`{H2^HwiM#U5sZ-;}t}a(&V_4(<6dPYd2A|DFt8nB{W0=bI@Th^9tSp|*izM9w zRZ!R4J2ozo0AAg!mA7V%vHq*C&C4tDcWpl&AY4Q-`L#=zlK(9c%Yu^Ce-mAYfJit9x{R%xKVPMZg9HS4jWjKU zG3bc!|Aqh0?r&%qn-_7QZ^8{)_-ocs(fbYfxg~+Fw}m0`LoxT~X3iX4!xkt+!0u~C0bt6CiTU3J<>j$zn=?X5wE6R?%1Rc85kRy@ zY=z4`0VSOW0E`FGUW3PA7hV{=2Kkl0~i&=fOY=UXjI)&%f`}_NO1r8c4Q7IHJy6C~r z{lh=|uHlvM`(K8F4h<8nvinz5)s6>Y9(cEsUt@kENifveYV3<3`UrFXeMAglL?;zG z95?QEI^PHe0EtLNmisHP@Sv{k4*Y|=z8oi@K z57GURA@RRbv$MGi8%dh#$5p-u5&_;+5{$ub>$U-4K&+TnRHTp)k8jz+WAgD6Cx&|> z<-LB4Q`o>nl|vDvfVqhK%aFG-nXc9gfMWc?g0YrvHN?J$GJ@shAz$HmmER{uq*u9a z0Mc$ByU7%(PHI%$?Dx6It3m>gM0J5?0HAZnWfx^3w#iBCX89fJcdQ4)CrnJ^MB(F_3e4k-YjwdS3cl9E0)^>`I5N~46y4GaP> z0rdu~vAH=Y24X0@hVbiqdA#DB^z?{$05Uo$DJd1aohtXBYVH+GRT+kuFBkvDUBvY4?68)Lfw3Z(CdsZMM1P_b0BEtE z?u(yy!s^HG6!1|PXTgb1`9#BKWrH4%E(w64unQt5{^fwjcj6h&P}2~`9PmbKk`4PF z(`BLH9Egbonn-|vH*5TEUY^?I2=s|BtWtC#zwbDFp=OjnXHT9?&!DU2J1EqvU(!rU zPJhC8q27Rt5IE~-ZKa}4UPJjlUN!YYlNQz?c9!%m)%d5|C&F!2-BW>rvpaLK}09Fkk2D*WWC1YkfU0m-{+Y8(m95bnqM+ z_?hA=J21xp1c4!_Ep-U-vckAH`LhWdEBSe(4e-uJzW{)qn1@}d+;90CRzFg~Bbg{( zHLq^{Ah2Vl)S}D@6bj}WyUzd!d*IDHgK^i48GacHq&RRzDJERLm+jR`H%4-+2)Ys9d_%1t4In9W~J;Uhw?>*#COAP5|T{F$AY8 zZ2(~?OrQ{EJB&ZLenP~{q(F#3;LE2kg%o%cy7TG~GhQ&j$m*Ai{|C_$Zv`dHGBbnr zCX&Dh>MFVzDj4ta?ci0zjVyLzQ2&B5of${e2@=B#0|Ef(AcaDGY$$-CMbB%wM*Er0_xXBI*q`L2b;zu3XjMv{aMAxx0 z)M)BO`1sdMn-*X}=+9Arv(gv@VgTeq>aDuA1q8r>fq^8rE|gzZd1wg;@SX|?bOGTc zcmW$Qu?85goB8t7MU?IybppEu&k9>8Vo^qV&Xb}4KhP;9&A>n)01z@v!;(OYc+w97 zx$z~&u;Ftiq`;i@oXrTLg2Hl2w{5zoY;nyoxf4FNGW2{!Hu9oP3MX238 z()c&~1H*0+mC|t#qka%zNSQhh6-YQ~i`*bdLP+vILd<+>3iTtXOEk-1j*JWyCWb}) zk2*lB)#cI(E}xV#`vZUrgWp-@bOtv`N)Q+Xg6IwKj>Hy_{7v1=S0h^aH>2n8k5MCG zOHVf%XfjmkJQHs6CptFhdIksS!!CaS7#bcFa4=efr&k=Rx^^=vE9oRoI(4-jtnvjbe)lK=AbxTA_MM|NOZEVP zL7<#GiAsV_eg_!*^i+eu{i?f15X>{^S!ffJg+=pn5|7 zT7YLp_*k;q^sjtk{;kMSB#HF60nu-8^((`>YyeWwZn3C}|G^`RJ1_u1&XIk8&Jj!w zDF{G{Vh>qHSXw0-)o_ zL51HO*_7~n005*P1acrqzbFhn+UL%hC!vr7gL3C@E?FYW185cf;aUA+BQFo$$p{T3 zya2)f1@r=(gM%Mn3(-+U`%XuP79&dUJ(}zOpRTGBK5m4G!r{$LP5PNHU&$d|{Y2Tr zDu8HEz13_74%ytSRu;Yov0|T*K=o-<9IBPUioN_=V)n|guINDWFvfTiTHxw&_ktDu zhO?6iRx9auRq>DasLq2TeEZY@;6=KyYovID>!lsx%KK!13HUxv+2&UNcoI^r66WjE z`?ww!V^O$%>V#H2ophTT0O*ZsCWt3uKzc!-7YJg2YLH?3GkovyCKP3o3u=t{a?x+J zHbLF^6ZRd&0Cq}u2Ek2u!XgYg?hYP+Hy{ClaUckvpC?+PfoS7KQ*(2;n6HDG#_vJG z$OAiJgA@RC^au>WQNiV>FbNWUuNMdbKlf2h4X=ES7Q)2InXjK@t6z79a!_UUgQ$wQ z4X2vCB?SPe5r*KXV?UpTI6mCkY9y&4%@bho)&EoY{Lw`4LS#9GNQi!^?nkjF>sLv} zA;Cwh7()&KsvG;ySYH`!&%}esr9n^?JnvxjQ$=zVk$4`#h;TALXweU;O-8fd)sItC zNu7Nt?+_yT|K5Z0=272Om|bJoBh$_@J}%0J!!a2>=uqUS(s5x_A7hx?1_=jRS!PeWI0czQ3e;NXmQzqF=5G zo`-ba>2y*gmpub}?IP@|g32b3j&%PJT)@|KpBvaSIJha*YW*$@3(MH!%cdaml$V$& z;{JFD{NnHF#5I*B&YSA$cqfu*rQ@*&uQ)k5qRf}Ce!-6)!fhUTw|984{tYS{IgZY) zy}g%GzYo3L!s}oRDy+uYn2gQc-L!d=M59C+h-+%D;3Ltx`b82YEX)TPVm=vtH-Y({ z0Dr&DJeNYRJ(XwT<33#@wZVNu>dRLcGv{3-~qf&X8<6`!B7k4Q?MzT31=cm z+*m2|_51ts+CHxD8&pc;Oh-IA)lm?=8aa)Y;gqkpUI31gYXa zF#iu=;(G`ddGRor_yRGpu>r)tQbpXiXAc)+(M*}hx1X7nC1Fa~nC~~Z^J;rL@7$_J zP~kn00@NVVt+QRczyJ`m-i#W75n^Xl9l*@oTpFYfc%S;;BW&CTWFna4t6DvJfJ7|b zL%?Fxp-2C~R`h5moMRA9=Ic)OQ11UHb#>BX;lYDv@S+-4wMxId+kFG1z*pg8#{1&q z{)#=Q5OW?4Hk#1-gj=J&k|g(-FTNNO1%(*VuL6{emHPQ|=Ia;zIBbLK{eHYJX$H{? z-d)cmWMy*df)w~GwZ3Jbc0~=kdX$;uDC@5BUz4L7Z}pe?C4iGgTNf^c7R~KE`2X&zTD`e z+JxU~aF;D&^&bu;0N6p7>tb{br9Uc4v;`xj6CfjMmv$A}(nKo(_mP7VNfj zUCgKK>l#@6n>8NI4_Dm@=6{Llq+n*b9qHoiLaTLlVSKzzv6{_h!>4&B0t6)h07bi^ zg^(O$$Kkm$91er4Ue0_E?x)r>|3qL-3*mIoA#`?o1d-c6MFId+H~0ohrIw(zX;Hk* zW*Wgz2@te{q6ix?JuVkt0pm@V<>b(jw!BHj21Ghtkb0$%fF@r%JuuKX^M%;I3oHM1 zjm$S;q}XhqKt-L$f*_NTL%xqoNZ7P?^5hasg!<|ZwpTtT`qo{$c<0jbpb3ak+>9;Z zC#I*5V(7J0>bYdvq zT$&)hqoyDaf&3wy&D8Gk=$U^veEw7LnU5GK;s78ji;evmj+47~eny5}BHn`r1bG}+ zq5k0GFsp<3;e(%XGAh|2x~rfY`ueDhoM$(CMljMcoCLGu)yMR1vEhUckRan)?_rTc z*b}(+fXgM0g^er#1XaeyKD)A@;JjIBX|`}3=}xiuYpbjImhDgzwpY^`f1LSfsc(Gq ztFki}Wl@RgzK$Mzz;HX#@Nx5I=GDi1>Iv{Vf^%mu(6|EtWGQwy{s}{AVR>@0iuGp< z6m*u*jQ~L?)PWgeUUD*L{JhjuA?l4YUas%OdpuLR*GfGD=q z;dmE5{OoL;RLR0fPh#`PFQ6}VqjcD4L$s07(2~_IVZ34Hi&XRS9Xm8L3yGe6F#0M* zv@d!9K$Hoa;N3?jO{yRr8cuo>nXy}u7ax!ZnRqkBbJ1*a379UPOS;-a!FUzsi%RP& zVc*D!SNJr+;G51{`GBGk07NB|Vq;%NcCRGO;ShBKVW8^HPAZP@yPp7EGw4Va&iILm ziBd)yXS{sX`=ghUSGl{sUJLUNgZXo)jFN$(8306SIDz~Pv{Ic>k(j99ekcI}H<6CE zrLuA|tmq&G&iH8%_0a2j7%peJ@l~(5PS5_w=4RDM9Ws1%VE$~L;ter{E8yZ6oI}N^jw%zXU|L8yTQsrK$Z!c#) zr$2%DaODD8`(XY~FrN&*E*5$UYYj#(N|#PW9{Zh*ZEd|OCxMFzH!zX`gk-zj|K0=r z8A?5j4w5qD^pwk(a zP9E_>-|MYI2p8j}_5hMg{=&Q6V9GgIIA7);3o}J5>5k1Mdy%nv3O$GyW?QXUg?76| z=ze3=0v+0fF(Wb2zaAIEXeSR{dS2Y8yoa`%{T%U(p%J0oG@Gr4+&s9pTUmrC(p49I z2x6?mj@4XDK=E<$@o`pRtOY%qFqy505>AbalQ26{jF;bcFPOh?V8FNPy$JBEN7>n_ zLf?Nr0YEea5WE5qyzq8s=kWkQT18x33^PF3VMMK6DH9h)rejE$uE+gblA20c$G*dE z_hyLuC7oXHT7j9*GXO*rio>mL_hYDctADe-edgr2xD<#$Q?Tt`VNLVAyk;`8isj;7 z^*Xwa46d%L+Xr>{eJq6Q1{MDO=NjWRrLcx)>fVrYjjg1jaY)b2>l3cls)eLNc)^r`-QZXKH$`CIt zS?|Z=37ytQ_!E!paW2DuDu5D@mnH8SBfak1Q|Ia?Nlq4liVNJdD+A6c1d&VfC}K7v5n#5I6Xkn-i%FS9LGy$2&I^At5pO(DnCSe zSl5ZCrQDND=&SfPtX2;`R$JYu0DcS9%6m-x*Bl2Q7&jAZpX+dU#*82#khXIY;a+=B z5T0(HY6*IJ!W${XXAu6*oxMJO#<5w23g9mwBILQ_{S)LinFU<2f>>K&Q)w|@EFn Date: Thu, 15 Sep 2016 20:05:06 -0400 Subject: [PATCH 194/350] Update credits --- data/CREDITS | Bin 16886 -> 16934 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/data/CREDITS b/data/CREDITS index d12f77486cf78d2bdfdd3535c15d6ebfb4ff3398..1307b2ce0c9879c0ff8dd6d171e36b1477c2dc31 100755 GIT binary patch delta 96 zcmey?%($$Dal;1w$p`o)Ed3Z>GNdplFnBTO0m+jOrfSyk|&0RXGd7wP~2 delta 44 zcmV+{0Mq}bgaP)00kE(Sljsi&0W6cP4;zyR5V8vj080R20CE6y0CSVK5R Date: Thu, 15 Sep 2016 21:12:01 -0400 Subject: [PATCH 195/350] Workaround crash --- src/graphics/texture_manager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/graphics/texture_manager.cpp b/src/graphics/texture_manager.cpp index 2fed716c1..5b823cf72 100644 --- a/src/graphics/texture_manager.cpp +++ b/src/graphics/texture_manager.cpp @@ -34,6 +34,8 @@ GLuint getTextureGLuint(irr::video::ITexture *tex) { + if (tex == NULL) + return 0; #if defined(USE_GLES2) return static_cast(tex)->getOpenGLTextureName(); #else From 05d2d05f2a617a73dfec760c074cb027deefd365 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 16 Sep 2016 09:22:57 +0800 Subject: [PATCH 196/350] Make ArenaAI use only new graph interface --- src/items/item.cpp | 23 ++-- src/items/item_manager.cpp | 15 ++- src/items/item_manager.hpp | 10 ++ src/karts/controller/arena_ai.cpp | 162 ++++++++++++++--------------- src/karts/controller/arena_ai.hpp | 10 +- src/karts/controller/soccer_ai.hpp | 2 +- src/tracks/track.cpp | 4 +- 7 files changed, 114 insertions(+), 112 deletions(-) diff --git a/src/items/item.cpp b/src/items/item.cpp index 01ed0d5d5..12d64d813 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -25,6 +25,7 @@ #include "modes/easter_egg_hunt.hpp" #include "modes/three_strikes_battle.hpp" #include "modes/world.hpp" +#include "tracks/arena_graph.hpp" #include "tracks/graph_node.hpp" #include "tracks/quad_graph.hpp" #include "tracks/track.hpp" @@ -134,23 +135,17 @@ void Item::initItem(ItemType type, const Vec3 &xyz) } // Now determine in which quad this item is, and its distance // from the center within this quad. - m_graph_node = QuadGraph::UNKNOWN_SECTOR; - QuadGraph* currentQuadGraph = QuadGraph::get(); + m_graph_node = Graph::UNKNOWN_SECTOR; + m_distance_from_center = 9999.9f; + m_avoidance_points[0] = NULL; + m_avoidance_points[1] = NULL; - // Check that QuadGraph exist (it might not in battle mode for eg) - if (currentQuadGraph != NULL) + // Check that Graph exist (it might not in battle mode without navmesh) + if (Graph::get()) { - QuadGraph::get()->findRoadSector(xyz, &m_graph_node); + Graph::get()->findRoadSector(xyz, &m_graph_node); } - - if(m_graph_node==QuadGraph::UNKNOWN_SECTOR) - { - m_graph_node = -1; - m_distance_from_center = 9999.9f; // is not used - m_avoidance_points[0] = NULL; - m_avoidance_points[1] = NULL; - } - else + else if (!ArenaGraph::get()) // Todo replace with driveline graph { // Item is on quad graph. Pre-compute the distance from center // of this item, which is used by the AI (mostly for avoiding items) diff --git a/src/items/item_manager.cpp b/src/items/item_manager.cpp index ab746ef74..3fb85525f 100644 --- a/src/items/item_manager.cpp +++ b/src/items/item_manager.cpp @@ -32,9 +32,10 @@ #include "modes/linear_world.hpp" #include "network/network_config.hpp" #include "network/race_event_manager.hpp" +#include "tracks/arena_graph.hpp" #include "tracks/battle_graph.hpp" +#include "tracks/graph.hpp" #include "tracks/graph_node.hpp" -#include "tracks/quad_graph.hpp" #include "tracks/track.hpp" #include "utils/string_utils.hpp" @@ -158,12 +159,12 @@ ItemManager::ItemManager() m_switch_to.push_back((Item::ItemType)i); setSwitchItems(stk_config->m_switch_items); - if(QuadGraph::get()) + if(Graph::get()) { m_items_in_quads = new std::vector; // Entries 0 to n-1 are for the quads, entry // n is for all items that are not on a quad. - m_items_in_quads->resize(QuadGraph::get()->getNumNodes()+1); + m_items_in_quads->resize(Graph::get()->getNumNodes()+1); } else { @@ -228,8 +229,7 @@ void ItemManager::insertItem(Item *item) // If the item is on the driveline, store it at the appropriate index if(graph_node > -1) { - int sector = QuadGraph::get()->getNode(graph_node)->getNodeIndex(); - (*m_items_in_quads)[sector].push_back(item); + (*m_items_in_quads)[graph_node].push_back(item); } else // otherwise store it in the 'outside' index (*m_items_in_quads)[m_items_in_quads->size()-1].push_back(item); @@ -427,9 +427,8 @@ void ItemManager::deleteItem(Item *item) if(m_items_in_quads) { const Vec3 &xyz = item->getXYZ(); - int sector = QuadGraph::UNKNOWN_SECTOR; - QuadGraph::get()->findRoadSector(xyz, §or); - unsigned int indx = sector==QuadGraph::UNKNOWN_SECTOR + int sector = item->getGraphNode(); + unsigned int indx = sector==Graph::UNKNOWN_SECTOR ? (unsigned int) m_items_in_quads->size()-1 : sector; AllItemTypes &items = (*m_items_in_quads)[indx]; diff --git a/src/items/item_manager.hpp b/src/items/item_manager.hpp index ac5f4f820..fb04308bc 100644 --- a/src/items/item_manager.hpp +++ b/src/items/item_manager.hpp @@ -132,6 +132,16 @@ public: assert(n<(*m_items_in_quads).size()); return (*m_items_in_quads)[n]; } // getItemsInQuads + // ------------------------------------------------------------------------ + /** Returns the first item (NULL if none) on the specified quad + */ + Item* getFirstItemInQuad(unsigned int n) const + { + assert(m_items_in_quads); + assert(n < m_items_in_quads->size()); + return ((*m_items_in_quads)[n]).empty() ? NULL : + (*m_items_in_quads)[n].front(); + } // getFirstItemInQuad }; // ItemManager #endif diff --git a/src/karts/controller/arena_ai.cpp b/src/karts/controller/arena_ai.cpp index 4c6fa3d7b..7bdf95192 100644 --- a/src/karts/controller/arena_ai.cpp +++ b/src/karts/controller/arena_ai.cpp @@ -26,11 +26,11 @@ #include "karts/controller/player_controller.hpp" #include "karts/controller/ai_properties.hpp" #include "karts/kart_properties.hpp" -#include "tracks/battle_graph.hpp" -#include "tracks/quad.hpp" +#include "tracks/arena_graph.hpp" +#include "tracks/arena_node.hpp" #include "utils/log.hpp" -int ArenaAI::m_test_node_for_banana = BattleGraph::UNKNOWN_POLY; +int ArenaAI::m_test_node_for_banana = Graph::UNKNOWN_SECTOR; bool isNodeWithBanana(const std::pair& item_pair) { @@ -44,6 +44,8 @@ ArenaAI::ArenaAI(AbstractKart *kart) { m_debug_sphere = NULL; m_debug_sphere_next = NULL; + m_graph = ArenaGraph::get(); + assert(m_graph != NULL); } // ArenaAI //----------------------------------------------------------------------------- @@ -51,18 +53,18 @@ ArenaAI::ArenaAI(AbstractKart *kart) */ void ArenaAI::reset() { - m_target_node = BattleGraph::UNKNOWN_POLY; - m_current_forward_node = BattleGraph::UNKNOWN_POLY; + m_target_node = Graph::UNKNOWN_SECTOR; + m_current_forward_node = Graph::UNKNOWN_SECTOR; m_current_forward_point = Vec3(0, 0, 0); m_adjusting_side = false; m_closest_kart = NULL; - m_closest_kart_node = BattleGraph::UNKNOWN_POLY; + m_closest_kart_node = Graph::UNKNOWN_SECTOR; m_closest_kart_point = Vec3(0, 0, 0); m_closest_kart_pos_data = {0}; m_cur_kart_pos_data = {0}; m_is_stuck = false; m_is_uturn = false; - m_avoiding_banana = false; + m_avoiding_item = false; m_target_point = Vec3(0, 0, 0); m_time_since_last_shot = 0.0f; m_time_since_driving = 0.0f; @@ -88,7 +90,7 @@ void ArenaAI::update(float dt) // This is used to enable firing an item backwards. m_controls->m_look_back = false; m_controls->m_nitro = false; - m_avoiding_banana = false; + m_avoiding_item = false; // Don't do anything if there is currently a kart animations shown. if (m_kart->getKartAnimation()) @@ -140,24 +142,29 @@ bool ArenaAI::updateAimingPosition() // to compensate the time difference between steering m_current_forward_point = m_kart->getTrans()(Vec3(0, 0, m_kart->getKartLength())); - m_current_forward_node = BattleGraph::get()->pointToNode - (m_current_forward_node, m_current_forward_point, - false/*ignore_vertical*/); + + std::vector* test_nodes = NULL; + if (m_current_forward_node != Graph::UNKNOWN_SECTOR) + { + test_nodes = + m_graph->getNode(m_current_forward_node)->getNearbyNodes(); + } + m_graph->findRoadSector(m_current_forward_point, &m_current_forward_node, + test_nodes); // Use current node if forward node is unknown, or near the target - const int forward = (m_current_forward_node == BattleGraph::UNKNOWN_POLY || + const int forward = (m_current_forward_node == Graph::UNKNOWN_SECTOR || m_current_forward_node == m_target_node || getCurrentNode() == m_target_node ? getCurrentNode() : m_current_forward_node); - if (forward == BattleGraph::UNKNOWN_POLY || - m_target_node == BattleGraph::UNKNOWN_POLY) + if (forward == Graph::UNKNOWN_SECTOR || + m_target_node == Graph::UNKNOWN_SECTOR) return false; if (forward == m_target_node) { - m_aiming_points.push_back(BattleGraph::get() - ->getQuadOfNode(forward).getCenter()); + m_aiming_points.push_back(m_graph->getNode(forward)->getCenter()); m_aiming_points.push_back(m_target_point); m_aiming_nodes.insert(forward); @@ -165,19 +172,16 @@ bool ArenaAI::updateAimingPosition() return true; } - const int next_node = BattleGraph::get() - ->getNextShortestPathPoly(forward, m_target_node); - if (next_node == BattleGraph::UNKNOWN_POLY) + const int next_node = m_graph->getNextNode(forward, m_target_node); + if (next_node == Graph::UNKNOWN_SECTOR) { Log::error("ArenaAI", "Next node is unknown, did you forget to link" "adjacent face in navmesh?"); return false; } - m_aiming_points.push_back(BattleGraph::get() - ->getQuadOfNode(forward).getCenter()); - m_aiming_points.push_back(BattleGraph::get() - ->getQuadOfNode(next_node).getCenter()); + m_aiming_points.push_back(m_graph->getNode(forward)->getCenter()); + m_aiming_points.push_back(m_graph->getNode(next_node)->getCenter()); m_aiming_nodes.insert(forward); m_aiming_nodes.insert(next_node); @@ -194,8 +198,8 @@ void ArenaAI::handleArenaSteering(const float dt) { const int current_node = getCurrentNode(); - if (current_node == BattleGraph::UNKNOWN_POLY || - m_target_node == BattleGraph::UNKNOWN_POLY) + if (current_node == Graph::UNKNOWN_SECTOR || + m_target_node == Graph::UNKNOWN_SECTOR) { return; } @@ -224,7 +228,7 @@ void ArenaAI::handleArenaSteering(const float dt) } else if (found_position) { - updateBananaLocation(); + updateBadItemLocation(); assert(m_aiming_points.size() == 2); updateTurnRadius(m_kart->getXYZ(), m_aiming_points[0], m_aiming_points[1]); @@ -368,44 +372,41 @@ bool ArenaAI::handleArenaUnstuck(const float dt) } // handleArenaUnstuck //----------------------------------------------------------------------------- -void ArenaAI::updateBananaLocation() +void ArenaAI::updateBadItemLocation() { - std::vector>& item_list = - BattleGraph::get()->getItemList(); - - std::set::iterator node = m_aiming_nodes.begin(); - while (node != m_aiming_nodes.end()) + // Check if any nodes AI will cross will meet bad items + for (const int& node : m_aiming_nodes) { - m_test_node_for_banana = *node; - std::vector>::iterator it = - std::find_if(item_list.begin(), item_list.end(), isNodeWithBanana); + assert(node != Graph::UNKNOWN_SECTOR); + Item* selected = ItemManager::get()->getFirstItemInQuad(node); - if (it != item_list.end()) + if (selected && !selected->wasCollected() && + (selected->getType() == Item::ITEM_BANANA || + selected->getType() == Item::ITEM_BUBBLEGUM || + selected->getType() == Item::ITEM_BUBBLEGUM_NOLOK)) { - Vec3 banana_lc; - checkPosition(it->first->getXYZ(), NULL, &banana_lc, + Vec3 bad_item_lc; + checkPosition(selected->getXYZ(), NULL, &bad_item_lc, true/*use_front_xyz*/); - // If satisfy the below condition, AI should not eat banana: - // banana_lc.z() < 0.0f, behind the kart - if (banana_lc.z() < 0.0f) + // If satisfy the below condition, AI should not be affected by it: + // bad_item_lc.z() < 0.0f, behind the kart + if (bad_item_lc.z() < 0.0f) { - node++; continue; } - // If the node AI will pass has a banana, adjust the aim position - banana_lc = (banana_lc.x() < 0 ? banana_lc + Vec3(5, 0, 0) : - banana_lc - Vec3(5, 0, 0)); - m_aiming_points[1] = m_kart->getTrans()(banana_lc); - m_avoiding_banana = true; + // If the node AI will pass has a bad item, adjust the aim position + bad_item_lc = (bad_item_lc.x() < 0 ? bad_item_lc + Vec3(5, 0, 0) : + bad_item_lc - Vec3(5, 0, 0)); + m_aiming_points[1] = m_kart->getTrans()(bad_item_lc); + m_avoiding_item = true; // Handle one banana only return; } - node++; } -} // updateBananaLocation +} // updateBadItemLocation //----------------------------------------------------------------------------- /** This function handles braking. It used the turn radius found by @@ -429,8 +430,8 @@ void ArenaAI::handleArenaBraking() m_controls->m_brake = false; - if (getCurrentNode() == BattleGraph::UNKNOWN_POLY || - m_target_node == BattleGraph::UNKNOWN_POLY) return; + if (getCurrentNode() == Graph::UNKNOWN_SECTOR || + m_target_node == Graph::UNKNOWN_SECTOR) return; if (m_aiming_points.empty()) return; @@ -658,55 +659,50 @@ void ArenaAI::handleArenaItems(const float dt) //----------------------------------------------------------------------------- void ArenaAI::collectItemInArena(Vec3* aim_point, int* target_node) const { - float distance = 99999.9f; - const std::vector< std::pair >& item_list = - BattleGraph::get()->getItemList(); - const unsigned int items_count = item_list.size(); + float distance = 999999.9f; + Item* selected = (*target_node == Graph::UNKNOWN_SECTOR ? NULL : + ItemManager::get()->getFirstItemInQuad(*target_node)); - if (item_list.empty()) + // Don't look for a new item unless it's collected or swapped + if (selected && !(selected->wasCollected() || + selected->getType() == Item::ITEM_BANANA || + selected->getType() == Item::ITEM_BUBBLEGUM || + selected->getType() == Item::ITEM_BUBBLEGUM_NOLOK)) { - // Notice: this should not happen, as it makes no sense - // for an arean without items, if so how can attack happen? - Log::fatal ("ArenaAI", - "AI can't find any items in the arena, " - "maybe there is something wrong with the navmesh, " - "make sure it lies closely to the ground."); + *aim_point = selected->getXYZ(); return; } - unsigned int closest_item_num = 0; - - for (unsigned int i = 0; i < items_count; ++i) + for (unsigned int i = 0; i < m_graph->getNumNodes(); i++) { - const Item* item = item_list[i].first; + Item* cur_item = ItemManager::get()->getFirstItemInQuad(i); + if (cur_item == NULL) continue; + if (cur_item->wasCollected() || + cur_item->getType() == Item::ITEM_BANANA || + cur_item->getType() == Item::ITEM_BUBBLEGUM || + cur_item->getType() == Item::ITEM_BUBBLEGUM_NOLOK) + continue; - if (item->wasCollected()) continue; - - if ((item->getType() == Item::ITEM_NITRO_BIG || - item->getType() == Item::ITEM_NITRO_SMALL) && + if ((cur_item->getType() == Item::ITEM_NITRO_BIG || + cur_item->getType() == Item::ITEM_NITRO_SMALL) && (m_kart->getEnergy() > m_kart->getKartProperties()->getNitroSmallContainer())) continue; // Ignore nitro when already has some - float test_distance = BattleGraph::get() - ->getDistance(item_list[i].second, getCurrentNode()); - if (test_distance <= distance && - (item->getType() == Item::ITEM_BONUS_BOX || - item->getType() == Item::ITEM_NITRO_BIG || - item->getType() == Item::ITEM_NITRO_SMALL)) + const int cur_node = cur_item->getGraphNode(); + assert(cur_node != Graph::UNKNOWN_SECTOR); + float test_distance = m_graph->getDistance(cur_node, getCurrentNode()); + if (test_distance <= distance) { - closest_item_num = i; + selected = cur_item; distance = test_distance; } } - const Item *item_selected = item_list[closest_item_num].first; - if (item_selected->getType() == Item::ITEM_BONUS_BOX || - item_selected->getType() == Item::ITEM_NITRO_BIG || - item_selected->getType() == Item::ITEM_NITRO_SMALL) + if (selected != NULL) { - *aim_point = item_selected->getXYZ(); - *target_node = item_list[closest_item_num].second; + *aim_point = selected->getXYZ(); + *target_node = selected->getGraphNode(); } else { diff --git a/src/karts/controller/arena_ai.hpp b/src/karts/controller/arena_ai.hpp index 40122e5b6..8496fb60d 100644 --- a/src/karts/controller/arena_ai.hpp +++ b/src/karts/controller/arena_ai.hpp @@ -27,7 +27,7 @@ #include "graphics/irr_driver.hpp" #endif -class Vec3; +class ArenaGraph; namespace irr { @@ -63,11 +63,13 @@ protected: /** The target point. */ Vec3 m_target_point; - bool m_avoiding_banana; + bool m_avoiding_item; void collectItemInArena(Vec3*, int*) const; float findAngleFrom3Edges(float a, float b, float c); private: + ArenaGraph* m_graph; + /** Used by handleArenaUTurn, it tells whether to do left or right * turning when steering is overridden. */ bool m_adjusting_side; @@ -115,7 +117,7 @@ private: void handleArenaUTurn(const float dt); bool handleArenaUnstuck(const float dt); bool updateAimingPosition(); - void updateBananaLocation(); + void updateBadItemLocation(); void updateTurnRadius(const Vec3& p1, const Vec3& p2, const Vec3& p3); virtual int getCurrentNode() const = 0; @@ -123,7 +125,7 @@ private: virtual void resetAfterStop() {}; virtual void findClosestKart(bool use_difficulty) = 0; virtual void findTarget() = 0; - virtual bool forceBraking() { return m_avoiding_banana; } + virtual bool forceBraking() { return m_avoiding_item; } virtual bool ignorePathFinding() { return false; } public: static int m_test_node_for_banana; diff --git a/src/karts/controller/soccer_ai.hpp b/src/karts/controller/soccer_ai.hpp index 016088e5b..edfd52f8d 100644 --- a/src/karts/controller/soccer_ai.hpp +++ b/src/karts/controller/soccer_ai.hpp @@ -67,7 +67,7 @@ private: virtual bool isWaiting() const; virtual bool canSkid(float steer_fraction) { return false; } virtual bool forceBraking() OVERRIDE - { return m_avoiding_banana || m_force_brake; } + { return m_avoiding_item || m_force_brake; } virtual bool ignorePathFinding() OVERRIDE { return m_overtake_ball || m_chasing_ball; } public: diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index d0899d386..a224a6379 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -280,7 +280,7 @@ void Track::reset() void Track::cleanup() { QuadGraph::destroy(); - BattleGraph::destroy(); + Graph::destroy(); ItemManager::destroy(); VAOManager::kill(); @@ -678,7 +678,7 @@ void Track::loadBattleGraph(const XMLNode &node) ArenaGraph* graph = new ArenaGraph(m_root+"navmesh.xml", &node); Graph::setGraph(graph); - if(BattleGraph::get()->getNumNodes()==0) + if(Graph::get()->getNumNodes()==0) { Log::warn("track", "No graph nodes defined for track '%s'\n", m_filename.c_str()); From 20e85aa781838bc5af70477671e3bc76f5659142 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 16 Sep 2016 11:08:19 +0800 Subject: [PATCH 197/350] Port all function from battle graph to new graph interface --- src/items/item.cpp | 2 +- src/items/item_manager.cpp | 55 ++- src/karts/controller/arena_ai.cpp | 9 - src/karts/controller/arena_ai.hpp | 1 - src/karts/controller/player_controller.cpp | 1 - src/tracks/battle_graph.cpp | 467 --------------------- src/tracks/battle_graph.hpp | 166 -------- src/tracks/navmesh.cpp | 133 ------ src/tracks/navmesh.hpp | 121 ------ src/tracks/track.cpp | 18 +- src/tracks/track.hpp | 2 +- 11 files changed, 42 insertions(+), 933 deletions(-) delete mode 100644 src/tracks/battle_graph.cpp delete mode 100644 src/tracks/battle_graph.hpp delete mode 100644 src/tracks/navmesh.cpp delete mode 100644 src/tracks/navmesh.hpp diff --git a/src/items/item.cpp b/src/items/item.cpp index 12d64d813..8da6fd775 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -145,7 +145,7 @@ void Item::initItem(ItemType type, const Vec3 &xyz) { Graph::get()->findRoadSector(xyz, &m_graph_node); } - else if (!ArenaGraph::get()) // Todo replace with driveline graph + if (Graph::get() && !ArenaGraph::get()) // Todo replace with driveline graph { // Item is on quad graph. Pre-compute the distance from center // of this item, which is used by the AI (mostly for avoiding items) diff --git a/src/items/item_manager.cpp b/src/items/item_manager.cpp index 3fb85525f..e1bc8999d 100644 --- a/src/items/item_manager.cpp +++ b/src/items/item_manager.cpp @@ -32,10 +32,9 @@ #include "modes/linear_world.hpp" #include "network/network_config.hpp" #include "network/race_event_manager.hpp" +#include "physics/triangle_mesh.hpp" #include "tracks/arena_graph.hpp" -#include "tracks/battle_graph.hpp" -#include "tracks/graph.hpp" -#include "tracks/graph_node.hpp" +#include "tracks/arena_node.hpp" #include "tracks/track.hpp" #include "utils/string_utils.hpp" @@ -426,7 +425,6 @@ void ItemManager::deleteItem(Item *item) // First check if the item needs to be removed from the items-in-quad list if(m_items_in_quads) { - const Vec3 &xyz = item->getXYZ(); int sector = item->getGraphNode(); unsigned int indx = sector==Graph::UNKNOWN_SECTOR ? (unsigned int) m_items_in_quads->size()-1 @@ -480,40 +478,41 @@ void ItemManager::switchItems() bool ItemManager::randomItemsForArena(const AlignedArray& pos) { if (!UserConfigParams::m_random_arena_item) return false; - if (!BattleGraph::get()) return false; + if (!ArenaGraph::get()) return false; + const ArenaGraph* ag = ArenaGraph::get(); std::vector used_location; std::vector invalid_location; for (unsigned int i = 0; i < pos.size(); i++) { // Load all starting positions of arena, so no items will be near them - int node = BattleGraph::get()->pointToNode(/*cur_node*/-1, - Vec3(pos[i].getOrigin()), /*ignore_vertical*/true); + int node = -1; + ag->findRoadSector(pos[i].getOrigin(), &node, NULL, true); assert(node != -1); used_location.push_back(node); invalid_location.push_back(node); } RandomGenerator random; - const unsigned int MIN_DIST = int(sqrt(BattleGraph::get()->getNumNodes())); + const unsigned int ALL_NODES = ag->getNumNodes(); + const unsigned int MIN_DIST = int(sqrt(ALL_NODES)); const unsigned int TOTAL_ITEM = MIN_DIST / 2; Log::info("[ItemManager]","Creating %d random items for arena", TOTAL_ITEM); for (unsigned int i = 0; i < TOTAL_ITEM; i++) { int chosen_node = -1; - const unsigned int total_node = BattleGraph::get()->getNumNodes(); while(true) { if (used_location.size() - pos.size() + - invalid_location.size() == total_node) + invalid_location.size() == ALL_NODES) { Log::warn("[ItemManager]","Can't place more random items! " "Use default item location."); return false; } - const int node = random.get(total_node); + const int node = random.get(ALL_NODES); // Check if tried std::vector::iterator it = std::find(invalid_location.begin(), @@ -522,7 +521,7 @@ bool ItemManager::randomItemsForArena(const AlignedArray& pos) continue; // Check if near edge - if (BattleGraph::get()->isNearEdge(node)) + if (ag->getNode(node)->isNearEdge()) { invalid_location.push_back(node); continue; @@ -532,8 +531,7 @@ bool ItemManager::randomItemsForArena(const AlignedArray& pos) for (unsigned int j = 0; j < used_location.size(); j++) { if (!found) continue; - float test_distance = BattleGraph::get() - ->getDistance(used_location[j], node); + float test_distance = ag->getDistance(used_location[j], node); found = test_distance > MIN_DIST; } if (found) @@ -566,10 +564,31 @@ bool ItemManager::randomItemsForArena(const AlignedArray& pos) Item::ItemType type = (j > BONUS_BOX ? Item::ITEM_BONUS_BOX : j > NITRO_BIG ? Item::ITEM_NITRO_BIG : j > NITRO_SMALL ? Item::ITEM_NITRO_SMALL : Item::ITEM_BANANA); - Vec3 loc = BattleGraph::get() - ->getQuadOfNode(used_location[i]).getCenter(); - Item* item = newItem(type, loc, Vec3(0, 1, 0)); - BattleGraph::get()->insertItems(item, used_location[i]); + + ArenaNode* an = ag->getNode(used_location[i]); + Vec3 loc = an->getCenter(); + Vec3 quad_normal = an->getNormal(); + loc += quad_normal; + + // Do a raycast to help place it fully on the surface + const Material* m; + Vec3 normal; + Vec3 hit_point; + const TriangleMesh& tm = + World::getWorld()->getTrack()->getTriangleMesh(); + bool success = tm.castRay(loc, an->getCenter() + (-10000*quad_normal), + &hit_point, &m, &normal); + + if (success) + { + newItem(type, hit_point, normal); + } + else + { + Log::warn("[ItemManager]","Raycast to surface failed" + "from node %d", used_location[i]); + newItem(type, an->getCenter(), quad_normal); + } } return true; diff --git a/src/karts/controller/arena_ai.cpp b/src/karts/controller/arena_ai.cpp index 7bdf95192..bc9bb9d4d 100644 --- a/src/karts/controller/arena_ai.cpp +++ b/src/karts/controller/arena_ai.cpp @@ -30,15 +30,6 @@ #include "tracks/arena_node.hpp" #include "utils/log.hpp" -int ArenaAI::m_test_node_for_banana = Graph::UNKNOWN_SECTOR; - -bool isNodeWithBanana(const std::pair& item_pair) -{ - return item_pair.second == ArenaAI::m_test_node_for_banana && - item_pair.first->getType() == Item::ITEM_BANANA && - !item_pair.first->wasCollected(); -} - ArenaAI::ArenaAI(AbstractKart *kart) : AIBaseController(kart) { diff --git a/src/karts/controller/arena_ai.hpp b/src/karts/controller/arena_ai.hpp index 8496fb60d..d0bb64819 100644 --- a/src/karts/controller/arena_ai.hpp +++ b/src/karts/controller/arena_ai.hpp @@ -128,7 +128,6 @@ private: virtual bool forceBraking() { return m_avoiding_item; } virtual bool ignorePathFinding() { return false; } public: - static int m_test_node_for_banana; ArenaAI(AbstractKart *kart); virtual ~ArenaAI() {}; virtual void update (float delta); diff --git a/src/karts/controller/player_controller.cpp b/src/karts/controller/player_controller.cpp index 75def44a5..c87b051e8 100644 --- a/src/karts/controller/player_controller.cpp +++ b/src/karts/controller/player_controller.cpp @@ -33,7 +33,6 @@ #include "modes/world.hpp" #include "race/history.hpp" #include "states_screens/race_gui_base.hpp" -#include "tracks/battle_graph.hpp" #include "utils/constants.hpp" #include "utils/log.hpp" #include "utils/translation.hpp" diff --git a/src/tracks/battle_graph.cpp b/src/tracks/battle_graph.cpp deleted file mode 100644 index e6ebcc2ab..000000000 --- a/src/tracks/battle_graph.cpp +++ /dev/null @@ -1,467 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009-2015 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, B - -#include "tracks/battle_graph.hpp" - -#include -#include -#include - -#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/quad.hpp" -#include "tracks/track.hpp" -#include "tracks/track_manager.hpp" -#include "utils/log.hpp" - -#include -#include - -const int BattleGraph::UNKNOWN_POLY = -1; -BattleGraph * BattleGraph::m_battle_graph = NULL; - -/** Constructor, Creates a navmesh, builds a graph from the navmesh and -* 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) -{ - m_items_on_graph.clear(); - - NavMesh::create(navmesh_file_name); - m_navmesh_file = navmesh_file_name; - buildGraph(NavMesh::get()); - - // Compute shortest distance from all nodes - for(unsigned int i=0; i < NavMesh::get()->getNumberOfQuads(); i++) - computeDijkstra(i); - - sortNearbyQuad(); - if (node && race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER) - loadGoalNodes(node); - -} // BattleGraph - -// ----------------------------------------------------------------------------- -/** Destructor, destroys NavMesh and the debug mesh if it exists */ -BattleGraph::~BattleGraph(void) -{ - NavMesh::destroy(); - - if(UserConfigParams::m_track_debug) - cleanupDebugMesh(); - GraphStructure::destroyRTT(); -} // ~BattleGraph - -// ---------------------------------------------------------------------------- -/** Builds a graph from an existing NavMesh. The graph is stored as an - * adjacency matrix. */ -void BattleGraph::buildGraph(NavMesh* navmesh) -{ - const unsigned int n_quads = navmesh->getNumberOfQuads(); - - m_distance_matrix = std::vector> - (n_quads, std::vector(n_quads, 9999.9f)); - for(unsigned int i = 0; i < n_quads; i++) - { - const Quad& cur_quad = navmesh->getQuad(i); - for (const int& adjacent : navmesh->getAdjacentQuads(i)) - { - Vec3 diff = navmesh->getQuad(adjacent).getCenter() - - cur_quad.getCenter(); - float distance = diff.length(); - m_distance_matrix[i][adjacent] = distance; - } - m_distance_matrix[i][i] = 0.0f; - } - - // Allocate and initialise the previous node data structure: - m_parent_poly = std::vector> - (n_quads, std::vector(n_quads, BattleGraph::UNKNOWN_POLY)); - for (unsigned int i = 0; i < n_quads; i++) - { - for (unsigned int j = 0; j < n_quads; 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 - -// ---------------------------------------------------------------------------- -/** 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 IndDistPair; - - class Shortest - { - public: - bool operator()(const IndDistPair &p1, const IndDistPair &p2) - { - return p1.second > p2.second; - } - }; - std::priority_queue, Shortest> queue; - IndDistPair begin(source, 0.0f); - queue.push(begin); - const unsigned int n=getNumNodes(); - std::vector 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; - - for (const int& adjacent : navmesh->getAdjacentQuads(cur_index)) - { - // 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() -{ - unsigned int n = getNumNodes(); - - // initialize m_parent_poly with unknown_poly so that if no path is found b/w i and j - // then m_parent_poly[i][j] = -1 (UNKNOWN_POLY) - // AI must check this - m_parent_poly = std::vector< std::vector > (n, std::vector(n,BattleGraph::UNKNOWN_POLY)); - for(unsigned int i=0; i=9899.9f) m_parent_poly[i][j]=-1; - else m_parent_poly[i][j] = i; - } - } - - for(unsigned int k=0; kgetNumberOfItems(); - - for (unsigned int i = 0; i < item_count; ++i) - { - const Item* item = item_manager->getItem(i); - Vec3 xyz = item->getXYZ(); - int polygon = BattleGraph::UNKNOWN_POLY; - - for (unsigned int j = 0; j < this->getNumNodes(); ++j) - { - if (getQuadOfNode(j).pointInside(xyz, false)) - polygon = j; - } - - if (polygon != BattleGraph::UNKNOWN_POLY) - { - m_items_on_graph.push_back(std::make_pair(item, polygon)); - Log::debug("BattleGraph","item number %d is on polygon %d", i, polygon); - } - else - Log::debug("BattleGraph","Can't map item number %d with a suitable polygon", i); - } -} // findItemsOnGraphNodes - -// ----------------------------------------------------------------------------- -int BattleGraph::pointToNode(const int cur_node, - const Vec3& cur_point, - bool ignore_vertical) const -{ - if (cur_node == BattleGraph::UNKNOWN_POLY) - { - // Try all nodes in the battle graph - for (unsigned int node = 0; node < this->getNumNodes(); node++) - { - const Quad& quad = this->getQuadOfNode(node); - if (quad.pointInside(cur_point, ignore_vertical)) - { - return node; - } - } - } - else - { - // Check if the point is still on the same node - const Quad& cur_quad = this->getQuadOfNode(cur_node); - if (cur_quad.pointInside(cur_point, ignore_vertical)) return cur_node; - - // If not then check all nearby quads (8 quads) - // Skip the same node - assert(cur_node == m_nearby_quads[cur_node][0]); - for (unsigned int i = 1; i < m_nearby_quads[0].size(); i++) - { - const int test_node = m_nearby_quads[cur_node][i]; - const Quad& quad = this->getQuadOfNode(test_node); - if (quad.pointInside(cur_point, ignore_vertical)) - { - return test_node; - } - } - - // Current node is still unkown: - // Calculated distance from saved node to current position, - // if it's close enough than use the saved node anyway, it - // may happen when the kart stays on the edge of obstacles - Vec3 diff = (cur_quad.getCenter() - cur_point); - float dist = diff.length(); - - if (dist < 3.0f) - return cur_node; - } - - return BattleGraph::UNKNOWN_POLY; -} // pointToNode - -// ----------------------------------------------------------------------------- -const bool BattleGraph::differentNodeColor(int n, NodeColor* c) const -{ - std::set::iterator it; - it = m_red_node.find(n); - if (it != m_red_node.end()) - { - *c = COLOR_RED; - return true; - } - - it = m_blue_node.find(n); - if (it != m_blue_node.end()) - { - *c = COLOR_BLUE; - return true; - } - return false; -} // differentNodeColor - -// ----------------------------------------------------------------------------- -void BattleGraph::loadGoalNodes(const XMLNode *node) -{ - m_red_node.clear(); - m_blue_node.clear(); - - const XMLNode *check_node = node->getNode("checks"); - for (unsigned int i = 0; i < check_node->getNumNodes(); i++) - { - const XMLNode *goal = check_node->getNode(i); - if (goal->getName() =="goal") - { - Vec3 p1, p2; - bool first_goal = false; - goal->get("first_goal", &first_goal); - goal->get("p1", &p1); - goal->get("p2", &p2); - - int first = pointToNode(/*cur_node*/-1, p1, true); - int last = pointToNode(/*cur_node*/-1, p2, true); - - first_goal ? m_blue_node.insert(first) : m_red_node.insert(first); - first_goal ? m_blue_node.insert(last) : m_red_node.insert(last); - while (first != last) - { - // Find all the nodes which connect the two points of - // goal, notice: only work if it's a straight line - first = getNextShortestPathPoly(first, last); - first_goal ? m_blue_node.insert(first) : - m_red_node.insert(first); - } - } - } -} // 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; im_distance_matrix.size(); i++) - { - for(unsigned int j=0; jm_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 dijkstra_path = getPathFromTo(i, j, parent_poly); - std::vector 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 BattleGraph::getPathFromTo(int from, int to, - const std::vector< std::vector< int > > parent_poly) -{ - std::vector path; - path.push_back(to); - while(from!=to) - { - to = parent_poly[from][to]; - path.push_back(to); - } - return path; -} // getPathFromTo - -// ---------------------------------------------------------------------------- -void BattleGraph::sortNearbyQuad() -{ - // Only try the nearby 8 quads - const unsigned int n = 8; - m_nearby_quads = std::vector< std::vector > - (this->getNumNodes(), std::vector(n, BattleGraph::UNKNOWN_POLY)); - - for (unsigned int i = 0; i < this->getNumNodes(); i++) - { - // Get the distance to all nodes at i - std::vector dist = m_distance_matrix[i]; - for (unsigned int j = 0; j < n; j++) - { - std::vector::iterator it = - std::min_element(dist.begin(), dist.end()); - const int pos = it - dist.begin(); - m_nearby_quads[i][j] = pos; - dist[pos] = 999999.0f; - } - } -} // sortNearbyQuad - -// ---------------------------------------------------------------------------- -void BattleGraph::set3DVerticesOfGraph(int i, video::S3DVertex *v, - const video::SColor &color) const -{ - NavMesh::get()->getQuad(i).getVertices(v, color); -} // set3DVerticesOfGraph diff --git a/src/tracks/battle_graph.hpp b/src/tracks/battle_graph.hpp deleted file mode 100644 index ed809fd22..000000000 --- a/src/tracks/battle_graph.hpp +++ /dev/null @@ -1,166 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009-2015 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, B - -#ifndef HEADER_BATTLE_GRAPH_HPP -#define HEADER_BATTLE_GRAPH_HPP - -#include -#include -#include - -#include "tracks/graph_structure.hpp" -#include "tracks/navmesh.hpp" - -class Item; -class ItemManager; -class XMLNode; - -/** -* \ingroup tracks -* -* \brief This class stores a graph constructed from the navigatoin mesh. -* It uses a 'simplified singleton' design pattern: it has a static create -* function to create exactly one instance, a destroy function, and a get -* function (that does not have the side effect of the 'normal singleton' -* design pattern to create an instance). -\ingroup tracks -*/ -class BattleGraph : public GraphStructure -{ - -private: - static BattleGraph *m_battle_graph; - - /** The actual graph data structure, it is an adjacency matrix */ - std::vector< std::vector< float > > m_distance_matrix; - /** The matrix that is used to store computed shortest paths */ - std::vector< std::vector< int > > m_parent_poly; - - std::vector< std::vector< int > > m_nearby_quads; - - /** Stores the name of the file containing the NavMesh data */ - std::string m_navmesh_file; - - std::vector< std::pair > m_items_on_graph; - - std::set m_red_node; - std::set m_blue_node; - - void buildGraph(NavMesh*); - void computeFloydWarshall(); - void loadGoalNodes(const XMLNode *node); - void sortNearbyQuad(); - - BattleGraph(const std::string &navmesh_file_name, const XMLNode *node=NULL); - ~BattleGraph(void); - - // ------------------------------------------------------------------------ - virtual void set3DVerticesOfGraph(int i, video::S3DVertex *v, - const video::SColor &color) const; - // ------------------------------------------------------------------------ - virtual void getGraphBoundingBox(Vec3 *min, Vec3 *max) const - { NavMesh::get()->getBoundingBox(min, max); } - // ------------------------------------------------------------------------ - virtual const bool isNodeInvisible(int n) const { return false; } - // ------------------------------------------------------------------------ - virtual const bool hasLapLine() const - { return false; } - // ------------------------------------------------------------------------ - virtual const bool differentNodeColor(int n, NodeColor* c) const; - void computeDijkstra(int n); - static std::vector 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) - { - assert(m_battle_graph==NULL); - m_battle_graph = new BattleGraph(navmesh_file_name, node); - } // create - // ------------------------------------------------------------------------ - /** Cleans up the BattleGraph instance if it exists */ - static void destroy() - { - if(m_battle_graph) - { - delete m_battle_graph; - m_battle_graph = NULL; - } - } // destroy - // ------------------------------------------------------------------------ - /** Returns the number of nodes in the BattleGraph (equal to the number - * of quads in the NavMesh - */ - virtual const unsigned int getNumNodes() const - { return NavMesh::get()->getNumberOfQuads(); } - // ------------------------------------------------------------------------ - /** Returns the distance between any two nodes */ - float getDistance(int from, int to) const - { - if (from == BattleGraph::UNKNOWN_POLY || - to == BattleGraph::UNKNOWN_POLY) - return 0.0f; - return m_distance_matrix[from][to]; - } - // ------------------------------------------------------------------------ - /** Returns the next polygon on the shortest path from i to j. - * Note: m_parent_poly[j][i] contains the parent of i on path from j to i, - * which is the next node on the path from i to j (undirected graph) - */ - int getNextShortestPathPoly(int i, int j) const - { - if (i == BattleGraph::UNKNOWN_POLY || j == BattleGraph::UNKNOWN_POLY) - return BattleGraph::UNKNOWN_POLY; - return m_parent_poly[j][i]; - } - // ------------------------------------------------------------------------ - std::vector>& getItemList() - { return m_items_on_graph; } - // ------------------------------------------------------------------------ - void insertItems(Item* item, int polygon) - { m_items_on_graph.push_back(std::make_pair(item, polygon)); } - // ------------------------------------------------------------------------ - /** Returns the quad that belongs to a node. */ - const Quad& getQuadOfNode(unsigned int n) const - { return NavMesh::get()->getQuad(n); } - // ------------------------------------------------------------------------ - /** Returns true if the quad lies near the edge, which means it doesn't - * have 4 adjacent quads. - */ - bool isNearEdge(unsigned int n) const - { return NavMesh::get()->getAdjacentQuads(n).size() != 4; } - // ------------------------------------------------------------------------ -}; //BattleGraph - -#endif diff --git a/src/tracks/navmesh.cpp b/src/tracks/navmesh.cpp deleted file mode 100644 index 74d525add..000000000 --- a/src/tracks/navmesh.cpp +++ /dev/null @@ -1,133 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009-2015 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "tracks/navmesh.hpp" - -#include "io/file_manager.hpp" -#include "io/xml_node.hpp" -#include "tracks/quad.hpp" -#include "utils/log.hpp" - -#include - -NavMesh *NavMesh::m_nav_mesh = NULL; - -/** Constructor, loads the mesh information from a given set of polygons - * from a navmesh.xml file. - * \param filename Name of the file containing all polygons - */ -NavMesh::NavMesh(const std::string &filename) -{ - - m_min = Vec3( 99999, 99999, 99999); - m_max = Vec3(-99999, -99999, -99999); - - XMLNode *xml = file_manager->createXMLTree(filename); - if (xml->getName() != "navmesh") - { - Log::error("NavMesh", "NavMesh is invalid."); - delete xml; - return; - } - - std::vector all_vertices; - for (unsigned int i = 0; i < xml->getNumNodes(); i++) - { - const XMLNode *xml_node = xml->getNode(i); - if (xml_node->getName() == "vertices") - { - for (unsigned int i = 0; i < xml_node->getNumNodes(); i++) - { - const XMLNode *xml_node_node = xml_node->getNode(i); - if (!(xml_node_node->getName() == "vertex")) - { - Log::error("NavMesh", "Unsupported type '%s' found" - "in '%s' - ignored.", - xml_node_node->getName().c_str(), filename.c_str()); - continue; - } - - // Reading vertices - Vec3 p; - readVertex(xml_node_node, &p); - m_max.max(p); - m_min.min(p); - all_vertices.push_back(p); - } - } - - if (xml_node->getName() == "faces") - { - for(unsigned int i = 0; i < xml_node->getNumNodes(); i++) - { - const XMLNode *xml_node_node = xml_node->getNode(i); - if (xml_node_node->getName() != "face") - { - Log::error("NavMesh", "Unsupported type '%s' found in '%s'" - " - ignored.", - xml_node_node->getName().c_str(), filename.c_str()); - continue; - } - - // Reading quads - std::vector quad_index; - std::vector adjacent_quad_index; - xml_node_node->get("indices", &quad_index); - xml_node_node->get("adjacents", &adjacent_quad_index); - assert(quad_index.size() == 4); - - m_adjacent_quads.push_back(adjacent_quad_index); - m_quads.push_back(new Quad( - all_vertices[quad_index[0]], all_vertices[quad_index[1]], - all_vertices[quad_index[2]], all_vertices[quad_index[3]])); - } - } - } - delete xml; - -} // NavMesh - -// ---------------------------------------------------------------------------- - -NavMesh::~NavMesh() -{ - for (unsigned int i = 0; i < m_quads.size(); i++) - { - delete m_quads[i]; - } - m_quads.clear(); -} // ~NavMesh - -// ---------------------------------------------------------------------------- -/** Reads the vertex information from an XMLNode */ -void NavMesh::readVertex(const XMLNode *xml, Vec3* result) const -{ - float x, y, z; - xml->get("x", &x); - xml->get("y", &y); - xml->get("z", &z); - Vec3 temp(x, y, z); - *result = temp; -} // readVertex - -// ---------------------------------------------------------------------------- -const Vec3& NavMesh::getCenterOfQuad(unsigned int n) const -{ - assert(m_quads.size() > 0 && n < m_quads.size()); - return m_quads[n]->getCenter(); -} // getCenterOfQuad diff --git a/src/tracks/navmesh.hpp b/src/tracks/navmesh.hpp deleted file mode 100644 index bf0f16451..000000000 --- a/src/tracks/navmesh.hpp +++ /dev/null @@ -1,121 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009-2015 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, B - -#ifndef HEADER_NAVMESH_HPP -#define HEADER_NAVMESH_HPP - -#include -#include - -#include "utils/vec3.hpp" - -class Quad; -class XMLNode; - -/** - * \brief This class stores a set of navigation quads. It uses a - * 'simplified singleton' design pattern: it has a static create function - * to create exactly one instance, a destroy function, and a get function - * (that does not have the side effect of the 'normal singleton' design - * pattern to create an instance). Besides saving on the if statement in - * get(), this is necessary since certain race modes might not have a - * navigation mesh at all (e.g. race mode). So get() returns NULL in this - * case, and this is tested where necessary. - * \ingroup tracks - */ -class NavMesh -{ - -private: - static NavMesh *m_nav_mesh; - - /** The 2d bounding box, used for hashing. */ - Vec3 m_min; - Vec3 m_max; - - /** The actual set of quads that constitute the nav mesh */ - std::vector m_quads; - - std::vector> m_adjacent_quads; - - void readVertex(const XMLNode *xml, Vec3* result) const; - // ------------------------------------------------------------------------ - NavMesh(const std::string &filename); - // ------------------------------------------------------------------------ - ~NavMesh(); - -public: - /** Creates a NavMesh instance. */ - static void create(const std::string &filename) - { - assert(m_nav_mesh == NULL); - m_nav_mesh = new NavMesh(filename); - } - // ------------------------------------------------------------------------ - /** Cleans up the nav mesh. It is possible that this function is called - * even if no instance exists (e.g. in race). So it is not an - * error if there is no instance. - */ - static void destroy() - { - if (m_nav_mesh) - { - delete m_nav_mesh; - m_nav_mesh = NULL; - } - } - // ------------------------------------------------------------------------ - /** Returns the one instance of this object. It is possible that there - * is no instance created (e.g. in normal race, since it doesn't have - * a nav mesh), so we don't assert that an instance exist, and we - * also don't create one if it doesn't exists. - */ - static NavMesh *get() { return m_nav_mesh; } - // ------------------------------------------------------------------------ - /** Return the minimum and maximum coordinates of this navmesh. */ - void getBoundingBox(Vec3 *min, Vec3 *max) { *min=m_min; *max=m_max; } - // ------------------------------------------------------------------------ - /** Returns a const reference to a quad */ - const Quad& getQuad(unsigned int n) const - { - assert(m_quads.size() > 0 && n < m_quads.size()); - return *(m_quads[n]); - } - // ------------------------------------------------------------------------ - /** Returns a const referece to a vector containing the indices - * of quads adjacent to a given quad - */ - const std::vector& getAdjacentQuads(unsigned int n) const - { - assert(m_adjacent_quads.size() > 0 && n < m_adjacent_quads.size() && - m_quads.size() == m_adjacent_quads.size()); - return m_adjacent_quads[n]; - } - // ------------------------------------------------------------------------ - /** Returns the total number of quads */ - unsigned int getNumberOfQuads() const - { - assert(m_quads.size() > 0); - return m_quads.size(); - } - // ------------------------------------------------------------------------ - /** Returns the center of a quad */ - const Vec3& getCenterOfQuad(unsigned int n) const; - -}; -#endif diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index a224a6379..4802bf798 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -55,7 +55,6 @@ #include "scriptengine/script_engine.hpp" #include "tracks/arena_graph.hpp" #include "tracks/bezier_curve.hpp" -#include "tracks/battle_graph.hpp" #include "tracks/check_manager.hpp" #include "tracks/graph_node.hpp" #include "tracks/model_definition_loader.hpp" @@ -672,9 +671,8 @@ void Track::startMusic() const /** Loads the quad graph for arena, i.e. the definition of all quads, and the * way they are connected to each other. Input file name is hardcoded for now */ -void Track::loadBattleGraph(const XMLNode &node) +void Track::loadArenaGraph(const XMLNode &node) { - BattleGraph::create(m_root+"navmesh.xml", &node); ArenaGraph* graph = new ArenaGraph(m_root+"navmesh.xml", &node); Graph::setGraph(graph); @@ -687,7 +685,7 @@ void Track::loadBattleGraph(const XMLNode &node) { loadMinimap(); } -} // loadBattleGraph +} // loadArenaGraph //----------------------------------------------------------------------------- /** Loads the quad graph, i.e. the definition of all quads, and the way @@ -1663,7 +1661,7 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id) // map to. if (!m_is_arena && !m_is_soccer && !m_is_cutscene) loadQuadGraph(mode_id, reverse_track); else if ((m_is_arena || m_is_soccer) && !m_is_cutscene && m_has_navmesh) - loadBattleGraph(*root); + loadArenaGraph(*root); ItemManager::create(); @@ -1879,16 +1877,6 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id) delete root; - if ((m_is_arena || m_is_soccer) && !m_is_cutscene && m_has_navmesh && !arena_random_item_created) - BattleGraph::get()->findItemsOnGraphNodes(); - - /*if (UserConfigParams::m_track_debug && - race_manager->getMinorMode()!=RaceManager::MINOR_MODE_3_STRIKES && - !m_is_cutscene) - { - QuadGraph::get()->createDebugMesh(); - }*/ - if (UserConfigParams::m_track_debug && Graph::get() && !m_is_cutscene) Graph::get()->createDebugMesh(); diff --git a/src/tracks/track.hpp b/src/tracks/track.hpp index 37f74c285..3e1ae9cf8 100644 --- a/src/tracks/track.hpp +++ b/src/tracks/track.hpp @@ -374,7 +374,7 @@ private: void loadTrackInfo(); void loadQuadGraph(unsigned int mode_id, const bool reverse); - void loadBattleGraph(const XMLNode &node); + void loadArenaGraph(const XMLNode &node); void convertTrackToBullet(scene::ISceneNode *node); bool loadMainTrack(const XMLNode &node); void loadMinimap(); From a0005d57e934daf9adc2f6fd544c2f1096d866e1 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 16 Sep 2016 12:21:38 +0800 Subject: [PATCH 198/350] Bugfix and better error handling in ArenaGraph --- src/tracks/arena_graph.cpp | 11 ++++++++--- src/tracks/graph.cpp | 20 +++++++++++--------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/tracks/arena_graph.cpp b/src/tracks/arena_graph.cpp index 9c1f65759..3691d5c0b 100644 --- a/src/tracks/arena_graph.cpp +++ b/src/tracks/arena_graph.cpp @@ -121,6 +121,7 @@ void ArenaGraph::loadNavmesh(const std::string &navmesh) all_vertices.push_back(p); } } + if (xml_node->getName() == "faces") { for (unsigned int i = 0; i < xml_node->getNumNodes(); i++) @@ -128,8 +129,8 @@ void ArenaGraph::loadNavmesh(const std::string &navmesh) const XMLNode *xml_node_node = xml_node->getNode(i); if (xml_node_node->getName() != "face") { - Log::error("NavMesh", "Unsupported type '%s' found in '%s'" - " - ignored.", + Log::error("ArenaGraph", "Unsupported type '%s'" + " found in '%s' - ignored.", xml_node_node->getName().c_str(), navmesh.c_str()); continue; } @@ -139,7 +140,11 @@ void ArenaGraph::loadNavmesh(const std::string &navmesh) std::vector adjacent_quad_index; xml_node_node->get("indices", &quad_index); xml_node_node->get("adjacents", &adjacent_quad_index); - assert(quad_index.size() == 4); + if (quad_index.size() != 4) + { + Log::error("ArenaGraph", "A Node in navmesh is not made" + " of quad, will only use the first 4 vertices"); + } createQuad(all_vertices[quad_index[0]], all_vertices[quad_index[1]], all_vertices[quad_index[2]], diff --git a/src/tracks/graph.cpp b/src/tracks/graph.cpp index df24d7a4b..4acfede90 100644 --- a/src/tracks/graph.cpp +++ b/src/tracks/graph.cpp @@ -430,17 +430,19 @@ void Graph::createQuad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, ai_ignore); } } - - Log::debug("Graph", "2d node created, normal: %f, %f, %f", - normal.x(), normal.y(), normal.z()); - if (is_arena) - { - q = new ArenaNode(p0, p1, p2, p3, normal, node_index); - } else { - q = new Node2D(p0, p1, p2, p3, normal, node_index, invisible, - ai_ignore); + Log::debug("Graph", "2d node created, normal: %f, %f, %f", + normal.x(), normal.y(), normal.z()); + if (is_arena) + { + q = new ArenaNode(p0, p1, p2, p3, normal, node_index); + } + else + { + q = new Node2D(p0, p1, p2, p3, normal, node_index, invisible, + ai_ignore); + } } m_all_nodes.push_back(q); From 606a5401d3777c1d1beff75e70935d51aa73de4c Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 17 Sep 2016 09:53:44 +0800 Subject: [PATCH 199/350] Allow starting position on an upside down surface in arena Todo: referee and make btKart cast ray at correct normal --- src/karts/kart.cpp | 5 ++++- src/modes/world_with_rank.cpp | 2 +- src/tracks/track.cpp | 34 ++++++++++++++++++++++++++++++---- src/tracks/track.hpp | 1 + 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 39c97d243..e5c752ad4 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -391,7 +391,10 @@ void Kart::reset() Vec3 front(0, 0, getKartLength()*0.5f); m_xyz_front = getTrans()(front); - m_terrain_info->update(getTrans().getBasis()); + // Base on update() below, require if starting point of kart is not near + // 0, 0, 0 (like in battle arena) + m_terrain_info->update(getTrans().getBasis(), + getTrans().getOrigin() + getTrans().getBasis() * Vec3(0, 0.3f, 0)); // Reset is also called when the kart is created, at which time // m_controller is not yet defined, so this has to be tested here. diff --git a/src/modes/world_with_rank.cpp b/src/modes/world_with_rank.cpp index f24ef58c3..6ce2814be 100644 --- a/src/modes/world_with_rank.cpp +++ b/src/modes/world_with_rank.cpp @@ -162,7 +162,7 @@ unsigned int WorldWithRank::getRescuePositionIndex(AbstractKart *kart) for(unsigned int k=0; kgetWorldKartId()==k) continue; - float abs_distance2 = (getKart(k)->getXYZ()-v).length2_2d(); + float abs_distance2 = (getKart(k)->getXYZ()-v).length2(); const float CLEAR_SPAWN_RANGE2 = 5*5; if( abs_distance2 < CLEAR_SPAWN_RANGE2) { diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index 4802bf798..f4531f0f6 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -56,7 +56,6 @@ #include "tracks/arena_graph.hpp" #include "tracks/bezier_curve.hpp" #include "tracks/check_manager.hpp" -#include "tracks/graph_node.hpp" #include "tracks/model_definition_loader.hpp" #include "tracks/node_3d.hpp" #include "tracks/quad_graph.hpp" @@ -687,6 +686,34 @@ void Track::loadArenaGraph(const XMLNode &node) } } // loadArenaGraph +//----------------------------------------------------------------------------- +btQuaternion Track::getArenaStartRotation(const Vec3& xyz, float heading) +{ + btQuaternion def_pos(Vec3(0, 1, 0), heading * DEGREE_TO_RAD); + if (!ArenaGraph::get()) + return def_pos; + + // Set the correct axis based on normal of the starting position + int node = Graph::UNKNOWN_SECTOR; + Graph::get()->findRoadSector(xyz, &node); + if (node == Graph::UNKNOWN_SECTOR) + { + Log::warn("track", "Starting position is not on ArenaGraph"); + return def_pos; + } + + const Vec3& normal = Graph::get()->getQuad(node)->getNormal(); + Vec3 axis = -normal.cross(Vec3(0, 1, 0)); + if (axis.length() == 0) + axis = Vec3(0, 0, 1); + + btQuaternion q(axis, normal.angle(Vec3(0, 1, 0))); + btMatrix3x3 m; + m.setRotation(q); + return btQuaternion(m.getColumn(1), heading * DEGREE_TO_RAD) * q; + +} // getArenaStartRotation + //----------------------------------------------------------------------------- /** Loads the quad graph, i.e. the definition of all quads, and the way * they are connected to each other. @@ -1961,9 +1988,8 @@ void Track::loadObjects(const XMLNode* root, const std::string& path, ModelDefin } m_start_transforms[position].setOrigin(xyz); - m_start_transforms[position].setRotation( - btQuaternion(btVector3(0,1,0), - h*DEGREE_TO_RAD ) ); + m_start_transforms[position] + .setRotation(getArenaStartRotation(xyz, h)); } else if (name == "camera") { diff --git a/src/tracks/track.hpp b/src/tracks/track.hpp index 3e1ae9cf8..4c7de76cf 100644 --- a/src/tracks/track.hpp +++ b/src/tracks/track.hpp @@ -375,6 +375,7 @@ private: void loadTrackInfo(); void loadQuadGraph(unsigned int mode_id, const bool reverse); void loadArenaGraph(const XMLNode &node); + btQuaternion getArenaStartRotation(const Vec3& xyz, float heading); void convertTrackToBullet(scene::ISceneNode *node); bool loadMainTrack(const XMLNode &node); void loadMinimap(); From d05cff09c86f47269459d011246e03487cad9394 Mon Sep 17 00:00:00 2001 From: Deve Date: Sat, 17 Sep 2016 01:26:52 +0200 Subject: [PATCH 200/350] Add a graphics restriction for BGRA format on android. It causes issues with particles transparency. --- data/graphical_restrictions.xml | 1 + src/graphics/graphics_restrictions.cpp | 3 +++ 2 files changed, 4 insertions(+) diff --git a/data/graphical_restrictions.xml b/data/graphical_restrictions.xml index 94d5026e4..67d0321f8 100644 --- a/data/graphical_restrictions.xml +++ b/data/graphical_restrictions.xml @@ -29,4 +29,5 @@ + diff --git a/src/graphics/graphics_restrictions.cpp b/src/graphics/graphics_restrictions.cpp index d660748b5..74f8a8709 100644 --- a/src/graphics/graphics_restrictions.cpp +++ b/src/graphics/graphics_restrictions.cpp @@ -355,6 +355,9 @@ public: #endif #ifdef BSD if(m_os!="bsd") return false; +#endif +#ifdef ANDROID + if(m_os!="android") return false; #endif } // m_os.size()>0 From 21d62ed40550722abb76d5a0f6c61876f785cb5a Mon Sep 17 00:00:00 2001 From: Deve Date: Sat, 17 Sep 2016 01:37:44 +0200 Subject: [PATCH 201/350] Check availability of color buffer float extension in GLES renderer --- src/graphics/central_settings.cpp | 14 +++++++++++++- src/graphics/central_settings.hpp | 2 ++ src/graphics/graphics_restrictions.cpp | 1 + src/graphics/graphics_restrictions.hpp | 1 + 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/graphics/central_settings.cpp b/src/graphics/central_settings.cpp index 3712cf008..8d17a1edc 100644 --- a/src/graphics/central_settings.cpp +++ b/src/graphics/central_settings.cpp @@ -52,6 +52,7 @@ void CentralVideoSettings::init() #if defined(USE_GLES2) hasBGRA = false; + hasColorBufferFloat = false; #endif m_GI_has_artifact = false; @@ -230,7 +231,13 @@ void CentralVideoSettings::init() hasBGRA = true; Log::info("GLDriver", "EXT texture format BGRA8888 Present"); } - + + if (!GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_COLOR_BUFFER_FLOAT) && + hasGLExtension("GL_EXT_color_buffer_float")) + { + hasColorBufferFloat = true; + Log::info("GLDriver", "EXT Color Buffer Float Present"); + } #endif } } @@ -355,6 +362,11 @@ bool CentralVideoSettings::isEXTTextureFormatBGRA8888Usable() const { return hasBGRA; } + +bool CentralVideoSettings::isEXTColorBufferFloatUsable() const +{ + return hasColorBufferFloat; +} #endif bool CentralVideoSettings::supportsShadows() const diff --git a/src/graphics/central_settings.hpp b/src/graphics/central_settings.hpp index 3553d7775..b9e8d1526 100644 --- a/src/graphics/central_settings.hpp +++ b/src/graphics/central_settings.hpp @@ -45,6 +45,7 @@ private: #if defined(USE_GLES2) bool hasBGRA; + bool hasColorBufferFloat; #endif bool m_need_rh_workaround; @@ -82,6 +83,7 @@ public: #if defined(USE_GLES2) bool isEXTTextureFormatBGRA8888Usable() const; + bool isEXTColorBufferFloatUsable() const; #endif diff --git a/src/graphics/graphics_restrictions.cpp b/src/graphics/graphics_restrictions.cpp index 74f8a8709..485f6755a 100644 --- a/src/graphics/graphics_restrictions.cpp +++ b/src/graphics/graphics_restrictions.cpp @@ -59,6 +59,7 @@ namespace GraphicsRestrictions "ExplicitAttribLocation", #if defined(USE_GLES2) "TextureFormatBGRA8888", + "ColorBufferFloat", #endif "DriverRecentEnough", "HighDefinitionTextures", diff --git a/src/graphics/graphics_restrictions.hpp b/src/graphics/graphics_restrictions.hpp index c17acf466..4ae195c6e 100644 --- a/src/graphics/graphics_restrictions.hpp +++ b/src/graphics/graphics_restrictions.hpp @@ -53,6 +53,7 @@ namespace GraphicsRestrictions GR_EXPLICIT_ATTRIB_LOCATION, #if defined(USE_GLES2) GR_TEXTURE_FORMAT_BGRA8888, + GR_COLOR_BUFFER_FLOAT, #endif GR_DRIVER_RECENT_ENOUGH, GR_HIGHDEFINITION_TEXTURES, From 852cc068d3951b61a1bd72967abe2550bb0b4c2f Mon Sep 17 00:00:00 2001 From: Deve Date: Sat, 17 Sep 2016 03:46:35 +0200 Subject: [PATCH 202/350] Restore SSAO on linux with GLES renderer. --- data/graphical_restrictions.xml | 1 + src/graphics/render.cpp | 2 -- src/graphics/render_lighting.cpp | 5 +++++ src/graphics/rtts.cpp | 23 +++++++++++++---------- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/data/graphical_restrictions.xml b/data/graphical_restrictions.xml index 67d0321f8..9d1374e55 100644 --- a/data/graphical_restrictions.xml +++ b/data/graphical_restrictions.xml @@ -30,4 +30,5 @@ + diff --git a/src/graphics/render.cpp b/src/graphics/render.cpp index 68e2c8cf2..3ef16eefc 100644 --- a/src/graphics/render.cpp +++ b/src/graphics/render.cpp @@ -431,10 +431,8 @@ void IrrDriver::renderScene(scene::ICameraSceneNode * const camnode, unsigned po { PROFILER_PUSH_CPU_MARKER("- SSAO", 0xFF, 0xFF, 0x00); ScopedGPUTimer Timer(getGPUTimer(Q_SSAO)); - #if !defined(USE_GLES2) if (UserConfigParams::m_ssao) renderSSAO(); - #endif PROFILER_POP_CPU_MARKER(); } diff --git a/src/graphics/render_lighting.cpp b/src/graphics/render_lighting.cpp index b90b9c9a2..cb5787f18 100644 --- a/src/graphics/render_lighting.cpp +++ b/src/graphics/render_lighting.cpp @@ -527,6 +527,11 @@ void IrrDriver::renderLights(unsigned pointlightcount, bool hasShadow) // ---------------------------------------------------------------------------- void IrrDriver::renderSSAO() { +#if defined(USE_GLES2) + if (!CVS->isEXTColorBufferFloatUsable()) + return; +#endif + m_rtts->getFBO(FBO_SSAO).bind(); glClearColor(1., 1., 1., 1.); glClear(GL_COLOR_BUFFER_BIT); diff --git a/src/graphics/rtts.cpp b/src/graphics/rtts.cpp index f0c9bbd94..a6040ab20 100644 --- a/src/graphics/rtts.cpp +++ b/src/graphics/rtts.cpp @@ -79,7 +79,6 @@ RTT::RTT(size_t width, size_t height) // All RTTs are currently RGBA16F mostly with stencil. The four tmp RTTs are the same size // as the screen, for use in post-processing. -#if !defined(USE_GLES2) GLint rgba_internal_format = GL_RGBA16F; GLint rgba_format = GL_BGRA; GLint red_internal_format = GL_R16F; @@ -88,15 +87,19 @@ RTT::RTT(size_t width, size_t height) GLint rgb_format = GL_BGR; GLint diffuse_specular_internal_format = GL_R11F_G11F_B10F; GLint type = GL_FLOAT; -#else - GLint rgba_internal_format = GL_RGBA8; - GLint rgba_format = GL_RGBA; - GLint red_internal_format = GL_R8; - GLint red32_internal_format = GL_R8; - GLint red_format = GL_RED; - GLint rgb_format = GL_RGB; - GLint diffuse_specular_internal_format = GL_RGBA8; - GLint type = GL_UNSIGNED_BYTE; + +#if defined(USE_GLES2) + if (!CVS->isEXTColorBufferFloatUsable()) + { + rgba_internal_format = GL_RGBA8; + rgba_format = GL_RGBA; + red_internal_format = GL_R8; + red32_internal_format = GL_R8; + red_format = GL_RED; + rgb_format = GL_RGB; + diffuse_specular_internal_format = GL_RGBA8; + type = GL_UNSIGNED_BYTE; + } #endif RenderTargetTextures[RTT_TMP1] = generateRTT(res, rgba_internal_format, rgba_format, type); From f2d354d759c7e8b029873a03366999971f17323e Mon Sep 17 00:00:00 2001 From: Deve Date: Sat, 17 Sep 2016 03:46:57 +0200 Subject: [PATCH 203/350] Fixed graphics restrictions on android --- src/graphics/graphics_restrictions.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/graphics/graphics_restrictions.cpp b/src/graphics/graphics_restrictions.cpp index 485f6755a..83b6be731 100644 --- a/src/graphics/graphics_restrictions.cpp +++ b/src/graphics/graphics_restrictions.cpp @@ -215,7 +215,7 @@ public: convertVersionString(s[0]); return; } - + Log::warn("Graphics", "Can not find version for '%s' '%s' - ignored.", driver_version.c_str(), card_name.c_str()); @@ -277,7 +277,7 @@ class Rule : public NoCopy { private: /** Operators to test for a card. */ - enum {CARD_IS, CARD_CONTAINS} m_card_test; + enum {CARD_IGNORE, CARD_IS, CARD_CONTAINS} m_card_test; /** Name of the card for which this rule applies. */ std::string m_card_name; @@ -298,6 +298,8 @@ public: Rule(const XMLNode *rule) { m_version_test = VERSION_IGNORE; + m_card_test = CARD_IGNORE; + if(rule->get("is", &m_card_name)) { m_card_test = CARD_IS; @@ -345,7 +347,7 @@ public: // ----------- if(m_os.size()>0) { -#ifdef __linux__ +#if defined(__linux__) && !defined(ANDROID) if(m_os!="linux") return false; #endif #ifdef WIN32 From eeac5668d902eec810b494d19ecbdd5f2303bbd7 Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 17 Sep 2016 14:30:28 +0800 Subject: [PATCH 204/350] Rename QuadGraph to DriveGraph, make it use the new Graph interface --- src/graphics/camera_end.cpp | 4 +- src/items/bowling.cpp | 2 - src/items/item.cpp | 14 +- src/items/item_manager.cpp | 2 +- src/items/item_manager.hpp | 2 +- src/items/rubber_ball.cpp | 26 +- src/items/rubber_ball.hpp | 1 - .../controller/ai_base_lap_controller.cpp | 38 +- .../controller/ai_base_lap_controller.hpp | 1 - src/karts/controller/end_controller.cpp | 34 +- src/karts/controller/end_controller.hpp | 1 - src/karts/controller/skidding_ai.cpp | 136 ++--- src/karts/controller/skidding_ai.hpp | 6 +- src/karts/controller/test_ai.cpp | 136 ++--- src/karts/controller/test_ai.hpp | 4 +- src/karts/kart.cpp | 18 +- src/modes/linear_world.cpp | 16 +- src/tracks/arena_graph.cpp | 3 +- src/tracks/arena_graph.hpp | 1 + src/tracks/check_manager.cpp | 4 +- .../{quad_graph.cpp => drive_graph.cpp} | 468 +++++------------- src/tracks/drive_graph.hpp | 125 +++++ src/tracks/{graph_node.cpp => drive_node.cpp} | 75 ++- src/tracks/{graph_node.hpp => drive_node.hpp} | 88 ++-- src/tracks/{node_2d.cpp => drive_node_2d.cpp} | 18 +- src/tracks/{node_2d.hpp => drive_node_2d.hpp} | 14 +- src/tracks/{node_3d.cpp => drive_node_3d.cpp} | 18 +- src/tracks/{node_3d.hpp => drive_node_3d.hpp} | 16 +- src/tracks/graph.cpp | 10 +- src/tracks/graph.hpp | 24 +- src/tracks/graph_structure.cpp | 386 --------------- src/tracks/graph_structure.hpp | 104 ---- src/tracks/quad_graph.hpp | 182 ------- src/tracks/track.cpp | 78 ++- src/tracks/track.hpp | 2 +- src/tracks/track_sector.cpp | 31 +- 36 files changed, 641 insertions(+), 1447 deletions(-) rename src/tracks/{quad_graph.cpp => drive_graph.cpp} (57%) create mode 100644 src/tracks/drive_graph.hpp rename src/tracks/{graph_node.cpp => drive_node.cpp} (73%) rename src/tracks/{graph_node.hpp => drive_node.hpp} (72%) rename src/tracks/{node_2d.cpp => drive_node_2d.cpp} (83%) rename src/tracks/{node_2d.hpp => drive_node_2d.hpp} (84%) rename src/tracks/{node_3d.cpp => drive_node_3d.cpp} (83%) rename src/tracks/{node_3d.hpp => drive_node_3d.hpp} (81%) delete mode 100644 src/tracks/graph_structure.cpp delete mode 100644 src/tracks/graph_structure.hpp delete mode 100644 src/tracks/quad_graph.hpp diff --git a/src/graphics/camera_end.cpp b/src/graphics/camera_end.cpp index 971daaa79..6b54d193f 100644 --- a/src/graphics/camera_end.cpp +++ b/src/graphics/camera_end.cpp @@ -20,7 +20,7 @@ #include "karts/abstract_kart.hpp" #include "karts/kart_properties.hpp" -#include "tracks/quad_graph.hpp" +#include "tracks/drive_graph.hpp" #include "ICameraSceneNode.h" @@ -62,7 +62,7 @@ void CameraEnd::readEndCamera(const XMLNode &root) unsigned int index = i; // In reverse mode, reverse the order in which the // end cameras are read. - if(QuadGraph::get()->isReverse()) + if(DriveGraph::get()->isReverse()) index = root.getNumNodes() - 1 - i; const XMLNode *node = root.getNode(index); EndCameraInformation eci; diff --git a/src/items/bowling.cpp b/src/items/bowling.cpp index e24a2ceb5..d2d5f0ee3 100644 --- a/src/items/bowling.cpp +++ b/src/items/bowling.cpp @@ -26,8 +26,6 @@ #include "karts/abstract_kart.hpp" #include "modes/linear_world.hpp" #include "utils/random_generator.hpp" -#include "tracks/graph_node.hpp" -#include "tracks/quad_graph.hpp" #include "utils/log.hpp" //TODO: remove after debugging is done diff --git a/src/items/item.cpp b/src/items/item.cpp index 8da6fd775..389154e2a 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -26,8 +26,8 @@ #include "modes/three_strikes_battle.hpp" #include "modes/world.hpp" #include "tracks/arena_graph.hpp" -#include "tracks/graph_node.hpp" -#include "tracks/quad_graph.hpp" +#include "tracks/drive_graph.hpp" +#include "tracks/drive_node.hpp" #include "tracks/track.hpp" #include "utils/constants.hpp" #include "utils/vec3.hpp" @@ -145,15 +145,15 @@ void Item::initItem(ItemType type, const Vec3 &xyz) { Graph::get()->findRoadSector(xyz, &m_graph_node); } - if (Graph::get() && !ArenaGraph::get()) // Todo replace with driveline graph + if (DriveGraph::get() && m_graph_node != Graph::UNKNOWN_SECTOR) { - // Item is on quad graph. Pre-compute the distance from center + // Item is on drive graph. Pre-compute the distance from center // of this item, which is used by the AI (mostly for avoiding items) Vec3 distances; - QuadGraph::get()->spatialToTrack(&distances, m_xyz, m_graph_node); + DriveGraph::get()->spatialToTrack(&distances, m_xyz, m_graph_node); m_distance_from_center = distances.getX(); - const GraphNode* gn = QuadGraph::get()->getNode(m_graph_node); - const Vec3& right = gn->getRightUnitVector(); + const DriveNode* dn = DriveGraph::get()->getNode(m_graph_node); + const Vec3& right = dn->getRightUnitVector(); // Give it 10% more space, since the kart will not always come // parallel to the drive line. Vec3 delta = right * sqrt(m_distance_2) * 1.3f; diff --git a/src/items/item_manager.cpp b/src/items/item_manager.cpp index e1bc8999d..61c5e3232 100644 --- a/src/items/item_manager.cpp +++ b/src/items/item_manager.cpp @@ -225,7 +225,7 @@ void ItemManager::insertItem(Item *item) if(m_items_in_quads) { int graph_node = item->getGraphNode(); - // If the item is on the driveline, store it at the appropriate index + // If the item is on the graph, store it at the appropriate index if(graph_node > -1) { (*m_items_in_quads)[graph_node].push_back(item); diff --git a/src/items/item_manager.hpp b/src/items/item_manager.hpp index fb04308bc..cba0bb814 100644 --- a/src/items/item_manager.hpp +++ b/src/items/item_manager.hpp @@ -82,7 +82,7 @@ private: /** Stores which items are on which quad. m_items_in_quads[#quads] * contains all items that are not on a quad. Note that this - * field is undefined if no QuadGraph exist, e.g. in battle mode. */ + * field is undefined if no Graph exist, e.g. arena without navmesh. */ std::vector< AllItemTypes > *m_items_in_quads; /** What item this item is switched to. */ diff --git a/src/items/rubber_ball.cpp b/src/items/rubber_ball.cpp index d322964c7..04c9e5ad5 100644 --- a/src/items/rubber_ball.cpp +++ b/src/items/rubber_ball.cpp @@ -29,8 +29,8 @@ #include "modes/linear_world.hpp" #include "physics/btKart.hpp" #include "physics/triangle_mesh.hpp" -#include "tracks/graph_node.hpp" -#include "tracks/quad_graph.hpp" +#include "tracks/drive_graph.hpp" +#include "tracks/drive_node.hpp" #include "tracks/track.hpp" #include "utils/log.hpp" //TODO: remove after debugging is done @@ -100,7 +100,7 @@ RubberBall::RubberBall(AbstractKart *kart) // initialises the current graph node TrackSector::update(getXYZ()); const Vec3& normal = - QuadGraph::get()->getNode(getCurrentGraphNode())->getNormal(); + DriveGraph::get()->getNode(getCurrentGraphNode())->getNormal(); TerrainInfo::update(getXYZ(), -normal); initializeControlPoints(m_owner->getXYZ()); @@ -136,7 +136,7 @@ void RubberBall::initializeControlPoints(const Vec3 &xyz) // left or right when firing the ball off track. getNextControlPoint(); m_control_points[2] = - QuadGraph::get()->getNode(m_last_aimed_graph_node)->getCenter(); + DriveGraph::get()->getNode(m_last_aimed_graph_node)->getCenter(); // This updates m_last_aimed_graph_node, and sets m_control_points[3] getNextControlPoint(); @@ -201,12 +201,12 @@ unsigned int RubberBall::getSuccessorToHitTarget(unsigned int node_index, unsigned int sect = lin_world->getSectorForKart(m_target); - succ = QuadGraph::get()->getNode(node_index)->getSuccessorToReach(sect); + succ = DriveGraph::get()->getNode(node_index)->getSuccessorToReach(sect); if(dist) - *dist += QuadGraph::get()->getNode(node_index) + *dist += DriveGraph::get()->getNode(node_index) ->getDistanceToSuccessor(succ); - return QuadGraph::get()->getNode(node_index)->getSuccessor(succ); + return DriveGraph::get()->getNode(node_index)->getSuccessor(succ); } // getSuccessorToHitTarget // ---------------------------------------------------------------------------- @@ -224,21 +224,21 @@ void RubberBall::getNextControlPoint() // spline between the control points. float dist=0; - float f = QuadGraph::get()->getDistanceFromStart(m_last_aimed_graph_node); + float f = DriveGraph::get()->getDistanceFromStart(m_last_aimed_graph_node); int next = getSuccessorToHitTarget(m_last_aimed_graph_node, &dist); - float d = QuadGraph::get()->getDistanceFromStart(next)-f; + float d = DriveGraph::get()->getDistanceFromStart(next)-f; while(d=0) { next = getSuccessorToHitTarget(next, &dist); - d = QuadGraph::get()->getDistanceFromStart(next)-f; + d = DriveGraph::get()->getDistanceFromStart(next)-f; } m_last_aimed_graph_node = next; m_length_cp_2_3 = dist; - const GraphNode* gn = - QuadGraph::get()->getNode(m_last_aimed_graph_node); - m_control_points[3] = gn->getCenter(); + const DriveNode* dn = + DriveGraph::get()->getNode(m_last_aimed_graph_node); + m_control_points[3] = dn->getCenter(); } // getNextControlPoint // ---------------------------------------------------------------------------- diff --git a/src/items/rubber_ball.hpp b/src/items/rubber_ball.hpp index 527ef3e59..2d354136b 100644 --- a/src/items/rubber_ball.hpp +++ b/src/items/rubber_ball.hpp @@ -25,7 +25,6 @@ #include "tracks/track_sector.hpp" class AbstractKart; -class QuadGraph; class SFXBase; /** diff --git a/src/karts/controller/ai_base_lap_controller.cpp b/src/karts/controller/ai_base_lap_controller.cpp index 23c5f0647..059ca0fd6 100644 --- a/src/karts/controller/ai_base_lap_controller.cpp +++ b/src/karts/controller/ai_base_lap_controller.cpp @@ -25,7 +25,7 @@ #include "karts/kart_properties.hpp" #include "karts/controller/ai_properties.hpp" #include "modes/linear_world.hpp" -#include "tracks/quad_graph.hpp" +#include "tracks/drive_graph.hpp" #include "utils/constants.hpp" @@ -37,7 +37,7 @@ I.e. the controller that takes over from a player (or AI) when the race is finished. This base class defines some basic operations: -- It takes care on which part of the QuadGraph the AI currently is. +- It takes care on which part of the DriveGraph the AI currently is. - It determines which path the AI should take (in case of shortcuts or forks in the road). @@ -139,14 +139,14 @@ void AIBaseLapController::newLap(int lap) */ void AIBaseLapController::computePath() { - m_next_node_index.resize(QuadGraph::get()->getNumNodes()); - m_successor_index.resize(QuadGraph::get()->getNumNodes()); + m_next_node_index.resize(DriveGraph::get()->getNumNodes()); + m_successor_index.resize(DriveGraph::get()->getNumNodes()); std::vector next; - for(unsigned int i=0; igetNumNodes(); i++) + for(unsigned int i=0; igetNumNodes(); i++) { next.clear(); // Get all successors the AI is allowed to take. - QuadGraph::get()->getSuccessors(i, next, /*for_ai*/true); + DriveGraph::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 @@ -154,7 +154,7 @@ void AIBaseLapController::computePath() // 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); + DriveGraph::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. @@ -171,12 +171,12 @@ void AIBaseLapController::computePath() // Now compute for each node in the graph the list of the next 'look_ahead' // graph nodes. This is the list of node that is tested in checkCrashes. // If the look_ahead is too big, the AI can skip loops (see - // QuadGraph::findRoadSector for details), if it's too short the AI won't + // Graph::findRoadSector for details), if it's too short the AI won't // find too good a driveline. Note that in general this list should // be computed recursively, but since the AI for now is using only // (randomly picked) path this is fine - m_all_look_aheads.resize(QuadGraph::get()->getNumNodes()); - for(unsigned int i=0; igetNumNodes(); i++) + m_all_look_aheads.resize(DriveGraph::get()->getNumNodes()); + for(unsigned int i=0; igetNumNodes(); i++) { std::vector l; int current = i; @@ -199,24 +199,24 @@ void AIBaseLapController::computePath() void AIBaseLapController::update(float dt) { AIBaseController::update(dt); - if(QuadGraph::get()) + if(DriveGraph::get()) { // Update the current node: int old_node = m_track_node; - if(m_track_node!=QuadGraph::UNKNOWN_SECTOR) + if(m_track_node!=Graph::UNKNOWN_SECTOR) { - QuadGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node, + DriveGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node, &m_all_look_aheads[m_track_node]); } // If we can't find a proper place on the track, to a broader search // on off-track locations. - if(m_track_node==QuadGraph::UNKNOWN_SECTOR) + if(m_track_node==Graph::UNKNOWN_SECTOR) { - m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ()); + m_track_node = DriveGraph::get()->findOutOfRoadSector(m_kart->getXYZ()); } // IF the AI is off track (or on a branch of the track it did not // select to be on), keep the old position. - if(m_track_node==QuadGraph::UNKNOWN_SECTOR || + if(m_track_node==Graph::UNKNOWN_SECTOR || m_next_node_index[m_track_node]==-1) m_track_node = old_node; } @@ -233,7 +233,7 @@ void AIBaseLapController::update(float dt) unsigned int AIBaseLapController::getNextSector(unsigned int index) { std::vector successors; - QuadGraph::get()->getSuccessors(index, successors); + DriveGraph::get()->getSuccessors(index, successors); return successors[0]; } // getNextSector @@ -245,8 +245,8 @@ unsigned int AIBaseLapController::getNextSector(unsigned int index) float AIBaseLapController::steerToAngle(const unsigned int sector, const float add_angle) { - float angle = QuadGraph::get()->getAngleToNext(sector, - getNextSector(sector)); + float angle = DriveGraph::get()->getAngleToNext(sector, + getNextSector(sector)); //Desired angle minus current angle equals how many angles to turn float steer_angle = angle - m_kart->getHeading(); diff --git a/src/karts/controller/ai_base_lap_controller.hpp b/src/karts/controller/ai_base_lap_controller.hpp index 0d616bd27..90d1feb26 100644 --- a/src/karts/controller/ai_base_lap_controller.hpp +++ b/src/karts/controller/ai_base_lap_controller.hpp @@ -22,7 +22,6 @@ class AIProperties; class LinearWorld; -class QuadGraph; class Track; class Vec3; diff --git a/src/karts/controller/end_controller.cpp b/src/karts/controller/end_controller.cpp index 7db4a0796..af696a188 100644 --- a/src/karts/controller/end_controller.cpp +++ b/src/karts/controller/end_controller.cpp @@ -45,8 +45,8 @@ #include "modes/linear_world.hpp" #include "race/race_manager.hpp" #include "states_screens/race_result_gui.hpp" -#include "tracks/graph_node.hpp" -#include "tracks/quad_graph.hpp" +#include "tracks/drive_graph.hpp" +#include "tracks/drive_node.hpp" #include "tracks/track.hpp" #include "utils/constants.hpp" #include "utils/log.hpp" @@ -63,14 +63,14 @@ EndController::EndController(AbstractKart *kart, // with a path that always picks the first branch (i.e. it follows // the main driveline). std::vector next; - for(unsigned int i=0; igetNumNodes(); i++) + for(unsigned int i=0; igetNumNodes(); i++) { // 0 is always a valid successor - so even if the kart should end // up by accident on a non-selected path, it will keep on working. m_successor_index[i] = 0; next.clear(); - QuadGraph::get()->getSuccessors(i, next); + DriveGraph::get()->getSuccessors(i, next); m_next_node_index[i] = next[0]; } @@ -78,11 +78,11 @@ EndController::EndController(AbstractKart *kart, // Now compute for each node in the graph the list of the next 'look_ahead' // graph nodes. This is the list of node that is tested in checkCrashes. // If the look_ahead is too big, the AI can skip loops (see - // QuadGraph::findRoadSector for details), if it's too short the AI won't + // DriveGraph::findRoadSector for details), if it's too short the AI won't // find too good a driveline. Note that in general this list should // be computed recursively, but since the AI for now is using only // (randomly picked) path this is fine - for(unsigned int i=0; igetNumNodes(); i++) + for(unsigned int i=0; igetNumNodes(); i++) { std::vector l; int current = i; @@ -95,7 +95,7 @@ EndController::EndController(AbstractKart *kart, } } // if not battle mode - // Reset must be called after QuadGraph::get() etc. is set up + // Reset must be called after DriveGraph::get() etc. is set up reset(); m_max_handicap_accel = 1.0f; @@ -130,18 +130,18 @@ void EndController::reset() m_crash_time = 0.0f; m_time_since_stuck = 0.0f; - m_track_node = QuadGraph::UNKNOWN_SECTOR; + m_track_node = Graph::UNKNOWN_SECTOR; // In battle mode there is no quad graph, so nothing to do in this case if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_3_STRIKES && race_manager->getMinorMode()!=RaceManager::MINOR_MODE_SOCCER) { - QuadGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node); + DriveGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node); // Node that this can happen quite easily, e.g. an AI kart is // taken over by the end controller while it is off track. - if(m_track_node==QuadGraph::UNKNOWN_SECTOR) + if(m_track_node==Graph::UNKNOWN_SECTOR) { - m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ()); + m_track_node = DriveGraph::get()->findOutOfRoadSector(m_kart->getXYZ()); } } } // reset @@ -212,10 +212,10 @@ void EndController::handleSteering(float dt) */ //Reaction to being outside of the road if( fabsf(m_world->getDistanceToCenterForKart( m_kart->getWorldKartId() )) > - 0.5f* QuadGraph::get()->getNode(m_track_node)->getPathWidth()+0.5f ) + 0.5f* DriveGraph::get()->getNode(m_track_node)->getPathWidth()+0.5f ) { const int next = m_next_node_index[m_track_node]; - target_point = QuadGraph::get()->getNode(next)->getCenter(); + target_point = DriveGraph::get()->getNode(next)->getCenter(); #ifdef AI_DEBUG Log::debug("end_controller.cpp", "- Outside of road: steer to center point."); #endif @@ -275,7 +275,7 @@ void EndController::findNonCrashingPoint(Vec3 *result) target_sector = m_next_node_index[sector]; //direction is a vector from our kart to the sectors we are testing - direction = QuadGraph::get()->getNode(target_sector)->getCenter() + direction = DriveGraph::get()->getNode(target_sector)->getCenter() - m_kart->getXYZ(); float len=direction.length(); @@ -293,16 +293,16 @@ void EndController::findNonCrashingPoint(Vec3 *result) { step_coord = m_kart->getXYZ()+direction*m_kart_length * float(i); - QuadGraph::get()->spatialToTrack(&step_track_coord, step_coord, + DriveGraph::get()->spatialToTrack(&step_track_coord, step_coord, sector ); distance = fabsf(step_track_coord[0]); //If we are outside, the previous sector is what we are looking for if ( distance + m_kart_width * 0.5f - > QuadGraph::get()->getNode(sector)->getPathWidth()*0.5f ) + > DriveGraph::get()->getNode(sector)->getPathWidth()*0.5f ) { - *result = QuadGraph::get()->getNode(sector)->getCenter(); + *result = DriveGraph::get()->getNode(sector)->getCenter(); return; } } diff --git a/src/karts/controller/end_controller.hpp b/src/karts/controller/end_controller.hpp index 96595c590..d9936edf3 100644 --- a/src/karts/controller/end_controller.hpp +++ b/src/karts/controller/end_controller.hpp @@ -25,7 +25,6 @@ class Camera; class LinearWorld; -class QuadGraph; class Track; class Vec3; diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index 1e443a56e..e370cea0a 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -41,7 +41,7 @@ #include "modes/linear_world.hpp" #include "modes/profile_world.hpp" #include "race/race_manager.hpp" -#include "tracks/quad_graph.hpp" +#include "tracks/drive_graph.hpp" #include "tracks/track.hpp" #include "utils/constants.hpp" #include "utils/log.hpp" @@ -171,22 +171,22 @@ void SkiddingAI::reset() m_distance_behind = 0.0f; m_current_curve_radius = 0.0f; m_curve_center = Vec3(0,0,0); - m_current_track_direction = GraphNode::DIR_STRAIGHT; + m_current_track_direction = DriveNode::DIR_STRAIGHT; m_item_to_collect = NULL; m_avoid_item_close = false; m_skid_probability_state = SKID_PROBAB_NOT_YET; m_last_item_random = NULL; AIBaseLapController::reset(); - m_track_node = QuadGraph::UNKNOWN_SECTOR; - QuadGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node); - if(m_track_node==QuadGraph::UNKNOWN_SECTOR) + m_track_node = Graph::UNKNOWN_SECTOR; + DriveGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node); + if(m_track_node==Graph::UNKNOWN_SECTOR) { Log::error(getControllerName().c_str(), "Invalid starting position for '%s' - not on track" " - can be ignored.", m_kart->getIdent().c_str()); - m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ()); + m_track_node = DriveGraph::get()->findOutOfRoadSector(m_kart->getXYZ()); } AIBaseLapController::reset(); @@ -414,7 +414,7 @@ void SkiddingAI::handleBraking() // If the kart is not facing roughly in the direction of the track, brake // so that it is easier for the kart to turn in the right direction. - if(m_current_track_direction==GraphNode::DIR_UNDEFINED && + if(m_current_track_direction==DriveNode::DIR_UNDEFINED && m_kart->getSpeed() > MIN_SPEED) { #ifdef DEBUG @@ -426,8 +426,8 @@ void SkiddingAI::handleBraking() m_controls->m_brake = true; return; } - if(m_current_track_direction==GraphNode::DIR_LEFT || - m_current_track_direction==GraphNode::DIR_RIGHT ) + if(m_current_track_direction==DriveNode::DIR_LEFT || + m_current_track_direction==DriveNode::DIR_RIGHT ) { float max_turn_speed = m_kart->getSpeedForTurnRadius(m_current_curve_radius); @@ -476,13 +476,13 @@ void SkiddingAI::handleSteering(float dt) m_world->getDistanceToCenterForKart( m_kart->getWorldKartId() ); if( fabsf(side_dist) > - 0.5f* QuadGraph::get()->getNode(m_track_node)->getPathWidth()+0.5f ) + 0.5f* DriveGraph::get()->getNode(m_track_node)->getPathWidth()+0.5f ) { - steer_angle = steerToPoint(QuadGraph::get()->getNode(next) + steer_angle = steerToPoint(DriveGraph::get()->getNode(next) ->getCenter()); #ifdef AI_DEBUG - m_debug_sphere[0]->setPosition(QuadGraph::get()->getNode(next) + m_debug_sphere[0]->setPosition(DriveGraph::get()->getNode(next) ->getCenter().toIrrVector()); Log::debug(getControllerName().c_str(), "Outside of road: steer to center point."); @@ -530,7 +530,7 @@ void SkiddingAI::handleSteering(float dt) { m_start_kart_crash_direction = 0; Vec3 aim_point; - int last_node = QuadGraph::UNKNOWN_SECTOR; + int last_node = Graph::UNKNOWN_SECTOR; switch(m_point_selection_algorithm) { @@ -623,7 +623,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, Vec3 kart_aim_direction = *aim_point - m_kart->getXYZ(); // Make sure we have a valid last_node - if(last_node==QuadGraph::UNKNOWN_SECTOR) + if(last_node==Graph::UNKNOWN_SECTOR) last_node = m_next_node_index[m_track_node]; int node = m_track_node; @@ -636,7 +636,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, const float max_item_lookahead_distance = 30.f; while(distance < max_item_lookahead_distance) { - int n_index= QuadGraph::get()->getNode(node)->getNodeIndex(); + int n_index= DriveGraph::get()->getNode(node)->getIndex(); const std::vector &items_ahead = ItemManager::get()->getItemsInQuads(n_index); for(unsigned int i=0; igetDistanceToNext(node, + distance += DriveGraph::get()->getDistanceToNext(node, m_successor_index[node]); node = m_next_node_index[node]; // Stop when we have reached the last quad @@ -781,7 +781,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, // so that it can potentially become a permanent target. Vec3 xyz = item_to_collect->getXYZ(); Vec3 item_direction = xyz - m_kart->getXYZ(); - Vec3 plane_normal = QuadGraph::get()->getNode(m_track_node) + Vec3 plane_normal = DriveGraph::get()->getNode(m_track_node) ->getNormal(); float dist_to_plane = item_direction.dot(plane_normal); Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; @@ -865,7 +865,7 @@ bool SkiddingAI::handleSelectedItem(Vec3 kart_aim_direction, Vec3 *aim_point) // the kart is. The current quad provides a good estimate of the kart's plane. const Vec3 &xyz = m_item_to_collect->getXYZ(); Vec3 item_direction = xyz - m_kart->getXYZ(); - Vec3 plane_normal = QuadGraph::get()->getNode(m_track_node)->getNormal(); + Vec3 plane_normal = DriveGraph::get()->getNode(m_track_node)->getNormal(); float dist_to_plane = item_direction.dot(plane_normal); Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; @@ -926,7 +926,7 @@ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, // rightmost point - if so, nothing to do. Vec3 left(items_to_avoid[index_left_most]->getXYZ()); int node_index = items_to_avoid[index_left_most]->getGraphNode(); - Vec3 normal = QuadGraph::get()->getNode(node_index)->getNormal(); + Vec3 normal = DriveGraph::get()->getNode(node_index)->getNormal(); Vec3 p1 = line_to_target.start, p2 = line_to_target.getMiddle() + normal.toIrrVector(), p3 = line_to_target.end; @@ -946,7 +946,7 @@ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, { Vec3 left(items_to_avoid[index_left_most]->getXYZ()); int node_index = items_to_avoid[index_left_most]->getGraphNode(); - Vec3 normal = QuadGraph::get()->getNode(node_index)->getNormal(); + Vec3 normal = DriveGraph::get()->getNode(node_index)->getNormal(); Vec3 p1 = line_to_target.start, p2 = line_to_target.getMiddle() + normal.toIrrVector(), p3 = line_to_target.end; @@ -1104,7 +1104,7 @@ void SkiddingAI::evaluateItems(const Item *item, Vec3 kart_aim_direction, // the kart is. The current quad provides a good estimate of the kart's plane. const Vec3 &xyz = item->getXYZ(); Vec3 item_direction = xyz - m_kart->getXYZ(); - Vec3 plane_normal = QuadGraph::get()->getNode(m_track_node)->getNormal(); + Vec3 plane_normal = DriveGraph::get()->getNode(m_track_node)->getNormal(); float dist_to_plane = item_direction.dot(plane_normal); Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; @@ -1714,14 +1714,14 @@ void SkiddingAI::handleNitroAndZipper() m_kart->getSpeed()>1.0f && m_kart->getSpeedIncreaseTimeLeft(MaxSpeed::MS_INCREASE_ZIPPER)<=0) { - GraphNode::DirectionType dir; + DriveNode::DirectionType dir; unsigned int last; - const GraphNode* gn = QuadGraph::get()->getNode(m_track_node); - gn->getDirectionData(m_successor_index[m_track_node], &dir, &last); - if(dir==GraphNode::DIR_STRAIGHT) + const DriveNode* dn = DriveGraph::get()->getNode(m_track_node); + dn->getDirectionData(m_successor_index[m_track_node], &dir, &last); + if(dir==DriveNode::DIR_STRAIGHT) { - float diff = QuadGraph::get()->getDistanceFromStart(last) - - QuadGraph::get()->getDistanceFromStart(m_track_node); + float diff = DriveGraph::get()->getDistanceFromStart(last) + - DriveGraph::get()->getDistanceFromStart(m_track_node); if(diff<0) diff+=World::getWorld()->getTrack()->getTrackLength(); if(diff>m_ai_properties->m_straight_length_for_zipper) m_controls->m_fire = true; @@ -1812,12 +1812,12 @@ void SkiddingAI::checkCrashes(const Vec3& pos ) } /*Find if we crash with the drivelines*/ - if(current_node!=QuadGraph::UNKNOWN_SECTOR && + if(current_node!=Graph::UNKNOWN_SECTOR && m_next_node_index[current_node]!=-1) - QuadGraph::get()->findRoadSector(step_coord, ¤t_node, + DriveGraph::get()->findRoadSector(step_coord, ¤t_node, /* sectors to test*/ &m_all_look_aheads[current_node]); - if( current_node == QuadGraph::UNKNOWN_SECTOR) + if( current_node == Graph::UNKNOWN_SECTOR) { m_crashes.m_road = true; return; @@ -1865,23 +1865,23 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node) *last_node = m_next_node_index[m_track_node]; const core::vector2df xz = m_kart->getXYZ().toIrrVector2d(); - const GraphNode* g = QuadGraph::get()->getNode(*last_node); + const DriveNode* dn = DriveGraph::get()->getNode(*last_node); // Index of the left and right end of a quad. const unsigned int LEFT_END_POINT = 0; const unsigned int RIGHT_END_POINT = 1; - core::line2df left (xz, (*g)[LEFT_END_POINT ].toIrrVector2d()); - core::line2df right(xz, (*g)[RIGHT_END_POINT].toIrrVector2d()); + core::line2df left (xz, (*dn)[LEFT_END_POINT ].toIrrVector2d()); + core::line2df right(xz, (*dn)[RIGHT_END_POINT].toIrrVector2d()); #if defined(AI_DEBUG) && defined(AI_DEBUG_NEW_FIND_NON_CRASHING) const Vec3 eps1(0,0.5f,0); m_curve[CURVE_LEFT]->clear(); m_curve[CURVE_LEFT]->addPoint(m_kart->getXYZ()+eps1); - m_curve[CURVE_LEFT]->addPoint((*g)[LEFT_END_POINT]+eps1); + m_curve[CURVE_LEFT]->addPoint((*dn)[LEFT_END_POINT]+eps1); m_curve[CURVE_LEFT]->addPoint(m_kart->getXYZ()+eps1); m_curve[CURVE_RIGHT]->clear(); m_curve[CURVE_RIGHT]->addPoint(m_kart->getXYZ()+eps1); - m_curve[CURVE_RIGHT]->addPoint((*g)[RIGHT_END_POINT]+eps1); + m_curve[CURVE_RIGHT]->addPoint((*dn)[RIGHT_END_POINT]+eps1); m_curve[CURVE_RIGHT]->addPoint(m_kart->getXYZ()+eps1); #endif #if defined(AI_DEBUG_KART_HEADING) || defined(AI_DEBUG_NEW_FIND_NON_CRASHING) @@ -1894,13 +1894,13 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node) while(1) { unsigned int next_sector = m_next_node_index[*last_node]; - const GraphNode* g_next = QuadGraph::get()->getNode(next_sector); + const DriveNode* dn_next = DriveGraph::get()->getNode(next_sector); // Test if the next left point is to the right of the left // line. If so, a new left line is defined. - if(left.getPointOrientation((*g_next)[LEFT_END_POINT].toIrrVector2d()) + if(left.getPointOrientation((*dn_next)[LEFT_END_POINT].toIrrVector2d()) < 0 ) { - core::vector2df p = (*g_next)[LEFT_END_POINT].toIrrVector2d(); + core::vector2df p = (*dn_next)[LEFT_END_POINT].toIrrVector2d(); // Stop if the new point is to the right of the right line if(right.getPointOrientation(p)<0) break; @@ -1916,10 +1916,10 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node) // Test if new right point is to the left of the right line. If // so, a new right line is defined. - if(right.getPointOrientation((*g_next)[RIGHT_END_POINT].toIrrVector2d()) + if(right.getPointOrientation((*dn_next)[RIGHT_END_POINT].toIrrVector2d()) > 0 ) { - core::vector2df p = (*g_next)[RIGHT_END_POINT].toIrrVector2d(); + core::vector2df p = (*dn_next)[RIGHT_END_POINT].toIrrVector2d(); // Break if new point is to the left of left line if(left.getPointOrientation(p)>0) break; @@ -1941,7 +1941,7 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node) // 0.5f*(left.end.Y+right.end.Y)); //*result = ppp; - *result = QuadGraph::get()->getNode(*last_node)->getCenter(); + *result = DriveGraph::get()->getNode(*last_node)->getCenter(); } // findNonCrashingPointNew //----------------------------------------------------------------------------- @@ -1978,7 +1978,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) target_sector = m_next_node_index[*last_node]; //direction is a vector from our kart to the sectors we are testing - direction = QuadGraph::get()->getNode(target_sector)->getCenter() + direction = DriveGraph::get()->getNode(target_sector)->getCenter() - m_kart->getXYZ(); float len=direction.length(); @@ -2001,23 +2001,23 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) { step_coord = m_kart->getXYZ()+direction*m_kart_length * float(i); - QuadGraph::get()->spatialToTrack(&step_track_coord, step_coord, + DriveGraph::get()->spatialToTrack(&step_track_coord, step_coord, *last_node ); float distance = fabsf(step_track_coord[0]); //If we are outside, the previous node is what we are looking for if ( distance + m_kart_width * 0.5f - > QuadGraph::get()->getNode(*last_node)->getPathWidth()*0.5f ) + > DriveGraph::get()->getNode(*last_node)->getPathWidth()*0.5f ) { - *aim_position = QuadGraph::get()->getNode(*last_node) + *aim_position = DriveGraph::get()->getNode(*last_node) ->getCenter(); return; } } *last_node = target_sector; } // for i<100 - *aim_position = QuadGraph::get()->getNode(*last_node)->getCenter(); + *aim_position = DriveGraph::get()->getNode(*last_node)->getCenter(); } // findNonCrashingPointFixed //----------------------------------------------------------------------------- @@ -2025,14 +2025,14 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) * 1. the test: * * distance + m_kart_width * 0.5f - * > QuadGraph::get()->getNode(*last_node)->getPathWidth() ) + * > DriveGraph::get()->getNode(*last_node)->getPathWidth() ) * * is incorrect, it should compare with getPathWith*0.5f (since distance * is the distance from the center, i.e. it is half the path width if * the point is at the edge). * 2. the test: * - * QuadGraph::get()->spatialToTrack(&step_track_coord, step_coord, + * DriveGraph::get()->spatialToTrack(&step_track_coord, step_coord, * *last_node ); * in the for loop tests always against distance from the same * graph node (*last_node), while de-fact the loop will test points @@ -2057,7 +2057,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) m_curve[CURVE_KART]->addPoint(m_kart->getTrans()(forw)+eps); #endif *last_node = m_next_node_index[m_track_node]; - float angle = QuadGraph::get()->getAngleToNext(m_track_node, + float angle = DriveGraph::get()->getAngleToNext(m_track_node, m_successor_index[m_track_node]); int target_sector; @@ -2073,7 +2073,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) // target_sector is the sector at the longest distance that we can // drive to without crashing with the track. target_sector = m_next_node_index[*last_node]; - angle1 = QuadGraph::get()->getAngleToNext(target_sector, + angle1 = DriveGraph::get()->getAngleToNext(target_sector, m_successor_index[target_sector]); // In very sharp turns this algorithm tends to aim at off track points, // resulting in hitting a corner. So test for this special case and @@ -2081,13 +2081,13 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) float diff = normalizeAngle(angle1-angle); if(fabsf(diff)>1.5f) { - *aim_position = QuadGraph::get()->getNode(target_sector) + *aim_position = DriveGraph::get()->getNode(target_sector) ->getCenter(); return; } //direction is a vector from our kart to the sectors we are testing - direction = QuadGraph::get()->getNode(target_sector)->getCenter() + direction = DriveGraph::get()->getNode(target_sector)->getCenter() - m_kart->getXYZ(); float len=direction.length(); @@ -2110,16 +2110,16 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) { step_coord = m_kart->getXYZ()+direction*m_kart_length * float(i); - QuadGraph::get()->spatialToTrack(&step_track_coord, step_coord, + DriveGraph::get()->spatialToTrack(&step_track_coord, step_coord, *last_node ); float distance = fabsf(step_track_coord[0]); //If we are outside, the previous node is what we are looking for if ( distance + m_kart_width * 0.5f - > QuadGraph::get()->getNode(*last_node)->getPathWidth() ) + > DriveGraph::get()->getNode(*last_node)->getPathWidth() ) { - *aim_position = QuadGraph::get()->getNode(*last_node) + *aim_position = DriveGraph::get()->getNode(*last_node) ->getCenter(); return; } @@ -2127,7 +2127,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) angle = angle1; *last_node = target_sector; } // for i<100 - *aim_position = QuadGraph::get()->getNode(*last_node)->getCenter(); + *aim_position = DriveGraph::get()->getNode(*last_node)->getCenter(); } // findNonCrashingPoint //----------------------------------------------------------------------------- @@ -2136,7 +2136,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) */ void SkiddingAI::determineTrackDirection() { - const QuadGraph *qg = QuadGraph::get(); + const DriveGraph *qg = DriveGraph::get(); unsigned int succ = m_successor_index[m_track_node]; unsigned int next = qg->getNode(m_track_node)->getSuccessor(succ); @@ -2165,7 +2165,7 @@ void SkiddingAI::determineTrackDirection() // quicker be aligned with the track again). if(fabsf(angle_to_track) > 0.22222f * M_PI) { - m_current_track_direction = GraphNode::DIR_UNDEFINED; + m_current_track_direction = DriveNode::DIR_UNDEFINED; return; } @@ -2181,8 +2181,8 @@ void SkiddingAI::determineTrackDirection() } #endif - if(m_current_track_direction==GraphNode::DIR_LEFT || - m_current_track_direction==GraphNode::DIR_RIGHT ) + if(m_current_track_direction==DriveNode::DIR_LEFT || + m_current_track_direction==DriveNode::DIR_RIGHT ) { handleCurve(); } // if(m_current_track_direction == DIR_LEFT || DIR_RIGHT ) @@ -2207,7 +2207,7 @@ void SkiddingAI::handleCurve() // kart will already point towards the direction of the circle), and // the case that the kart is facing wrong was already tested for before - const QuadGraph *qg = QuadGraph::get(); + const DriveGraph *qg = DriveGraph::get(); Vec3 xyz = m_kart->getXYZ(); Vec3 tangent = m_kart->getTrans()(Vec3(0,0,1)) - xyz; Vec3 last_xyz = qg->getNode(m_last_direction_node)->getCenter(); @@ -2227,7 +2227,7 @@ void SkiddingAI::handleCurve() { i = m_next_node_index[i]; // Pick either the lower left or right point: - int index = m_current_track_direction==GraphNode::DIR_LEFT + int index = m_current_track_direction==DriveNode::DIR_LEFT ? 0 : 1; float r = (m_curve_center - *(qg->getNode(i))[index]).length(); if(m_current_curve_radius < r) @@ -2286,8 +2286,8 @@ bool SkiddingAI::canSkid(float steer_fraction) } // No skidding on straights - if(m_current_track_direction==GraphNode::DIR_STRAIGHT || - m_current_track_direction==GraphNode::DIR_UNDEFINED ) + if(m_current_track_direction==DriveNode::DIR_STRAIGHT || + m_current_track_direction==DriveNode::DIR_UNDEFINED ) { #ifdef DEBUG if(m_controls->m_skid && m_ai_debug) @@ -2301,7 +2301,7 @@ bool SkiddingAI::canSkid(float steer_fraction) } const float MIN_SKID_SPEED = 5.0f; - const QuadGraph *qg = QuadGraph::get(); + const DriveGraph *qg = DriveGraph::get(); Vec3 last_xyz = qg->getNode(m_last_direction_node)->getCenter(); // Only try skidding when a certain minimum speed is reached. @@ -2312,7 +2312,7 @@ bool SkiddingAI::canSkid(float steer_fraction) Vec3 diff_last = last_xyz - m_curve_center; float angle_kart = atan2(diff_kart.getX(), diff_kart.getZ()); float angle_last = atan2(diff_last.getX(), diff_last.getZ()); - float angle = m_current_track_direction == GraphNode::DIR_RIGHT + float angle = m_current_track_direction == DriveNode::DIR_RIGHT ? angle_last - angle_kart : angle_kart - angle_last; angle = normalizeAngle(angle); @@ -2339,9 +2339,9 @@ bool SkiddingAI::canSkid(float steer_fraction) // left turn steer right to avoid getting too close to the left // vorder). In this case skidding will be useless. else if( (steer_fraction > 0 && - m_current_track_direction==GraphNode::DIR_LEFT) || + m_current_track_direction==DriveNode::DIR_LEFT) || (steer_fraction < 0 && - m_current_track_direction==GraphNode::DIR_RIGHT) ) + m_current_track_direction==DriveNode::DIR_RIGHT) ) { #ifdef DEBUG if(m_controls->m_skid && m_ai_debug) diff --git a/src/karts/controller/skidding_ai.hpp b/src/karts/controller/skidding_ai.hpp index cfbbd4863..503755e05 100644 --- a/src/karts/controller/skidding_ai.hpp +++ b/src/karts/controller/skidding_ai.hpp @@ -45,11 +45,11 @@ #include "karts/controller/ai_base_lap_controller.hpp" #include "race/race_manager.hpp" -#include "tracks/graph_node.hpp" +#include "tracks/drive_node.hpp" #include "utils/random_generator.hpp" class LinearWorld; -class QuadGraph; +class DriveGraph; class ShowCurve; class Track; @@ -155,7 +155,7 @@ private: int m_start_kart_crash_direction; /** The direction of the track where the kart is on atm. */ - GraphNode::DirectionType m_current_track_direction; + DriveNode::DirectionType m_current_track_direction; /** The radius of the curve the kart is currently driving. Undefined * when being on a straigt section. */ diff --git a/src/karts/controller/test_ai.cpp b/src/karts/controller/test_ai.cpp index e2004a484..1a57101fa 100644 --- a/src/karts/controller/test_ai.cpp +++ b/src/karts/controller/test_ai.cpp @@ -41,7 +41,7 @@ #include "modes/linear_world.hpp" #include "modes/profile_world.hpp" #include "race/race_manager.hpp" -#include "tracks/quad_graph.hpp" +#include "tracks/drive_graph.hpp" #include "tracks/track.hpp" #include "utils/constants.hpp" #include "utils/log.hpp" @@ -177,22 +177,22 @@ void SkiddingAI::reset() m_distance_behind = 0.0f; m_current_curve_radius = 0.0f; m_curve_center = Vec3(0,0,0); - m_current_track_direction = GraphNode::DIR_STRAIGHT; + m_current_track_direction = DriveNode::DIR_STRAIGHT; m_item_to_collect = NULL; m_avoid_item_close = false; m_skid_probability_state = SKID_PROBAB_NOT_YET; m_last_item_random = NULL; AIBaseLapController::reset(); - m_track_node = QuadGraph::UNKNOWN_SECTOR; - QuadGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node); - if(m_track_node==QuadGraph::UNKNOWN_SECTOR) + m_track_node = Graph::UNKNOWN_SECTOR; + DriveGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node); + if(m_track_node==Graph::UNKNOWN_SECTOR) { Log::error(getControllerName().c_str(), "Invalid starting position for '%s' - not on track" " - can be ignored.", m_kart->getIdent().c_str()); - m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ()); + m_track_node = DriveGraph::get()->findOutOfRoadSector(m_kart->getXYZ()); } AIBaseLapController::reset(); @@ -420,7 +420,7 @@ void SkiddingAI::handleBraking() // If the kart is not facing roughly in the direction of the track, brake // so that it is easier for the kart to turn in the right direction. - if(m_current_track_direction==GraphNode::DIR_UNDEFINED && + if(m_current_track_direction==DriveNode::DIR_UNDEFINED && m_kart->getSpeed() > MIN_SPEED) { #ifdef DEBUG @@ -432,8 +432,8 @@ void SkiddingAI::handleBraking() m_controls->m_brake = true; return; } - if(m_current_track_direction==GraphNode::DIR_LEFT || - m_current_track_direction==GraphNode::DIR_RIGHT ) + if(m_current_track_direction==DriveNode::DIR_LEFT || + m_current_track_direction==DriveNode::DIR_RIGHT ) { float max_turn_speed = m_kart->getSpeedForTurnRadius(m_current_curve_radius); @@ -482,13 +482,13 @@ void SkiddingAI::handleSteering(float dt) m_world->getDistanceToCenterForKart( m_kart->getWorldKartId() ); if( fabsf(side_dist) > - 0.5f* QuadGraph::get()->getNode(m_track_node)->getPathWidth()+0.5f ) + 0.5f* DriveGraph::get()->getNode(m_track_node)->getPathWidth()+0.5f ) { - steer_angle = steerToPoint(QuadGraph::get()->getNode(next) + steer_angle = steerToPoint(DriveGraph::get()->getNode(next) ->getCenter()); #ifdef AI_DEBUG - m_debug_sphere[0]->setPosition(QuadGraph::get()->getNode(next) + m_debug_sphere[0]->setPosition(DriveGraph::get()->getNode(next) ->getCenter().toIrrVector()); Log::debug(getControllerName().c_str(), "Outside of road: steer to center point."); @@ -536,7 +536,7 @@ void SkiddingAI::handleSteering(float dt) { m_start_kart_crash_direction = 0; Vec3 aim_point; - int last_node = QuadGraph::UNKNOWN_SECTOR; + int last_node = Graph::UNKNOWN_SECTOR; switch(m_point_selection_algorithm) { @@ -629,7 +629,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, Vec3 kart_aim_direction = *aim_point - m_kart->getXYZ(); // Make sure we have a valid last_node - if(last_node==QuadGraph::UNKNOWN_SECTOR) + if(last_node==Graph::UNKNOWN_SECTOR) last_node = m_next_node_index[m_track_node]; int node = m_track_node; @@ -642,7 +642,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, const float max_item_lookahead_distance = 30.f; while(distance < max_item_lookahead_distance) { - int n_index= QuadGraph::get()->getNode(node)->getNodeIndex(); + int n_index= DriveGraph::get()->getNode(node)->getIndex(); const std::vector &items_ahead = ItemManager::get()->getItemsInQuads(n_index); for(unsigned int i=0; igetDistanceToNext(node, + distance += DriveGraph::get()->getDistanceToNext(node, m_successor_index[node]); node = m_next_node_index[node]; // Stop when we have reached the last quad @@ -787,7 +787,7 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, // so that it can potentially become a permanent target. Vec3 xyz = item_to_collect->getXYZ(); Vec3 item_direction = xyz - m_kart->getXYZ(); - Vec3 plane_normal = QuadGraph::get()->getNode(m_track_node) + Vec3 plane_normal = DriveGraph::get()->getNode(m_track_node) ->getNormal(); float dist_to_plane = item_direction.dot(plane_normal); Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; @@ -871,7 +871,7 @@ bool SkiddingAI::handleSelectedItem(Vec3 kart_aim_direction, Vec3 *aim_point) // the kart is. The current quad provides a good estimate of the kart's plane. const Vec3 &xyz = m_item_to_collect->getXYZ(); Vec3 item_direction = xyz - m_kart->getXYZ(); - Vec3 plane_normal = QuadGraph::get()->getNode(m_track_node)->getNormal(); + Vec3 plane_normal = DriveGraph::get()->getNode(m_track_node)->getNormal(); float dist_to_plane = item_direction.dot(plane_normal); Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; @@ -932,7 +932,7 @@ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, // rightmost point - if so, nothing to do. Vec3 left(items_to_avoid[index_left_most]->getXYZ()); int node_index = items_to_avoid[index_left_most]->getGraphNode(); - Vec3 normal = QuadGraph::get()->getNode(node_index)->getNormal(); + Vec3 normal = DriveGraph::get()->getNode(node_index)->getNormal(); Vec3 p1 = line_to_target.start, p2 = line_to_target.getMiddle() + normal.toIrrVector(), p3 = line_to_target.end; @@ -952,7 +952,7 @@ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, { Vec3 left(items_to_avoid[index_left_most]->getXYZ()); int node_index = items_to_avoid[index_left_most]->getGraphNode(); - Vec3 normal = QuadGraph::get()->getNode(node_index)->getNormal(); + Vec3 normal = DriveGraph::get()->getNode(node_index)->getNormal(); Vec3 p1 = line_to_target.start, p2 = line_to_target.getMiddle() + normal.toIrrVector(), p3 = line_to_target.end; @@ -1110,7 +1110,7 @@ void SkiddingAI::evaluateItems(const Item *item, Vec3 kart_aim_direction, // the kart is. The current quad provides a good estimate of the kart's plane. const Vec3 &xyz = item->getXYZ(); Vec3 item_direction = xyz - m_kart->getXYZ(); - Vec3 plane_normal = QuadGraph::get()->getNode(m_track_node)->getNormal(); + Vec3 plane_normal = DriveGraph::get()->getNode(m_track_node)->getNormal(); float dist_to_plane = item_direction.dot(plane_normal); Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; @@ -1763,14 +1763,14 @@ void SkiddingAI::handleNitroAndZipper() m_kart->getSpeed()>1.0f && m_kart->getSpeedIncreaseTimeLeft(MaxSpeed::MS_INCREASE_ZIPPER)<=0) { - GraphNode::DirectionType dir; + DriveNode::DirectionType dir; unsigned int last; - const GraphNode* gn = QuadGraph::get()->getNode(m_track_node); - gn->getDirectionData(m_successor_index[m_track_node], &dir, &last); - if(dir==GraphNode::DIR_STRAIGHT) + const DriveNode* dn = DriveGraph::get()->getNode(m_track_node); + dn->getDirectionData(m_successor_index[m_track_node], &dir, &last); + if(dir==DriveNode::DIR_STRAIGHT) { - float diff = QuadGraph::get()->getDistanceFromStart(last) - - QuadGraph::get()->getDistanceFromStart(m_track_node); + float diff = DriveGraph::get()->getDistanceFromStart(last) + - DriveGraph::get()->getDistanceFromStart(m_track_node); if(diff<0) diff+=World::getWorld()->getTrack()->getTrackLength(); if(diff>m_ai_properties->m_straight_length_for_zipper) m_controls->m_fire = true; @@ -1861,12 +1861,12 @@ void SkiddingAI::checkCrashes(const Vec3& pos ) } /*Find if we crash with the drivelines*/ - if(current_node!=QuadGraph::UNKNOWN_SECTOR && + if(current_node!=Graph::UNKNOWN_SECTOR && m_next_node_index[current_node]!=-1) - QuadGraph::get()->findRoadSector(step_coord, ¤t_node, + DriveGraph::get()->findRoadSector(step_coord, ¤t_node, /* sectors to test*/ &m_all_look_aheads[current_node]); - if( current_node == QuadGraph::UNKNOWN_SECTOR) + if( current_node == Graph::UNKNOWN_SECTOR) { m_crashes.m_road = true; return; @@ -1914,23 +1914,23 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node) *last_node = m_next_node_index[m_track_node]; const core::vector2df xz = m_kart->getXYZ().toIrrVector2d(); - const GraphNode* g = QuadGraph::get()->getNode(*last_node); + const DriveNode* dn = DriveGraph::get()->getNode(*last_node); // Index of the left and right end of a quad. const unsigned int LEFT_END_POINT = 0; const unsigned int RIGHT_END_POINT = 1; - core::line2df left (xz, (*g)[LEFT_END_POINT ].toIrrVector2d()); - core::line2df right(xz, (*g)[RIGHT_END_POINT].toIrrVector2d()); + core::line2df left (xz, (*dn)[LEFT_END_POINT ].toIrrVector2d()); + core::line2df right(xz, (*dn)[RIGHT_END_POINT].toIrrVector2d()); #if defined(AI_DEBUG) && defined(AI_DEBUG_NEW_FIND_NON_CRASHING) const Vec3 eps1(0,0.5f,0); m_curve[CURVE_LEFT]->clear(); m_curve[CURVE_LEFT]->addPoint(m_kart->getXYZ()+eps1); - m_curve[CURVE_LEFT]->addPoint((*g)[LEFT_END_POINT]+eps1); + m_curve[CURVE_LEFT]->addPoint((*dn)[LEFT_END_POINT]+eps1); m_curve[CURVE_LEFT]->addPoint(m_kart->getXYZ()+eps1); m_curve[CURVE_RIGHT]->clear(); m_curve[CURVE_RIGHT]->addPoint(m_kart->getXYZ()+eps1); - m_curve[CURVE_RIGHT]->addPoint((*g)[RIGHT_END_POINT]+eps1); + m_curve[CURVE_RIGHT]->addPoint((*dn)[RIGHT_END_POINT]+eps1); m_curve[CURVE_RIGHT]->addPoint(m_kart->getXYZ()+eps1); #endif #if defined(AI_DEBUG_KART_HEADING) || defined(AI_DEBUG_NEW_FIND_NON_CRASHING) @@ -1943,13 +1943,13 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node) while(1) { unsigned int next_sector = m_next_node_index[*last_node]; - const GraphNode* g_next = QuadGraph::get()->getNode(next_sector); + const DriveNode* dn_next = DriveGraph::get()->getNode(next_sector); // Test if the next left point is to the right of the left // line. If so, a new left line is defined. - if(left.getPointOrientation((*g_next)[LEFT_END_POINT].toIrrVector2d()) + if(left.getPointOrientation((*dn_next)[LEFT_END_POINT].toIrrVector2d()) < 0 ) { - core::vector2df p = (*g_next)[LEFT_END_POINT].toIrrVector2d(); + core::vector2df p = (*dn_next)[LEFT_END_POINT].toIrrVector2d(); // Stop if the new point is to the right of the right line if(right.getPointOrientation(p)<0) break; @@ -1965,10 +1965,10 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node) // Test if new right point is to the left of the right line. If // so, a new right line is defined. - if(right.getPointOrientation((*g_next)[RIGHT_END_POINT].toIrrVector2d()) + if(right.getPointOrientation((*dn_next)[RIGHT_END_POINT].toIrrVector2d()) > 0 ) { - core::vector2df p = (*g_next)[RIGHT_END_POINT].toIrrVector2d(); + core::vector2df p = (*dn_next)[RIGHT_END_POINT].toIrrVector2d(); // Break if new point is to the left of left line if(left.getPointOrientation(p)>0) break; @@ -1990,7 +1990,7 @@ void SkiddingAI::findNonCrashingPointNew(Vec3 *result, int *last_node) // 0.5f*(left.end.Y+right.end.Y)); //*result = ppp; - *result = QuadGraph::get()->getNode(*last_node)->getCenter(); + *result = DriveGraph::get()->getNode(*last_node)->getCenter(); } // findNonCrashingPointNew //----------------------------------------------------------------------------- @@ -2027,7 +2027,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) target_sector = m_next_node_index[*last_node]; //direction is a vector from our kart to the sectors we are testing - direction = QuadGraph::get()->getNode(target_sector)->getCenter() + direction = DriveGraph::get()->getNode(target_sector)->getCenter() - m_kart->getXYZ(); float len=direction.length(); @@ -2050,23 +2050,23 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) { step_coord = m_kart->getXYZ()+direction*m_kart_length * float(i); - QuadGraph::get()->spatialToTrack(&step_track_coord, step_coord, + DriveGraph::get()->spatialToTrack(&step_track_coord, step_coord, *last_node ); float distance = fabsf(step_track_coord[0]); //If we are outside, the previous node is what we are looking for if ( distance + m_kart_width * 0.5f - > QuadGraph::get()->getNode(*last_node)->getPathWidth()*0.5f ) + > DriveGraph::get()->getNode(*last_node)->getPathWidth()*0.5f ) { - *aim_position = QuadGraph::get()->getNode(*last_node) + *aim_position = DriveGraph::get()->getNode(*last_node) ->getCenter(); return; } } *last_node = target_sector; } // for i<100 - *aim_position = QuadGraph::get()->getNode(*last_node)->getCenter(); + *aim_position = DriveGraph::get()->getNode(*last_node)->getCenter(); } // findNonCrashingPointFixed //----------------------------------------------------------------------------- @@ -2074,14 +2074,14 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) * 1. the test: * * distance + m_kart_width * 0.5f - * > QuadGraph::get()->getNode(*last_node)->getPathWidth() ) + * > DriveGraph::get()->getNode(*last_node)->getPathWidth() ) * * is incorrect, it should compare with getPathWith*0.5f (since distance * is the distance from the center, i.e. it is half the path width if * the point is at the edge). * 2. the test: * - * QuadGraph::get()->spatialToTrack(&step_track_coord, step_coord, + * DriveGraph::get()->spatialToTrack(&step_track_coord, step_coord, * *last_node ); * in the for loop tests always against distance from the same * graph node (*last_node), while de-fact the loop will test points @@ -2106,7 +2106,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) m_curve[CURVE_KART]->addPoint(m_kart->getTrans()(forw)+eps); #endif *last_node = m_next_node_index[m_track_node]; - float angle = QuadGraph::get()->getAngleToNext(m_track_node, + float angle = DriveGraph::get()->getAngleToNext(m_track_node, m_successor_index[m_track_node]); int target_sector; @@ -2122,7 +2122,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) // target_sector is the sector at the longest distance that we can // drive to without crashing with the track. target_sector = m_next_node_index[*last_node]; - angle1 = QuadGraph::get()->getAngleToNext(target_sector, + angle1 = DriveGraph::get()->getAngleToNext(target_sector, m_successor_index[target_sector]); // In very sharp turns this algorithm tends to aim at off track points, // resulting in hitting a corner. So test for this special case and @@ -2130,13 +2130,13 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) float diff = normalizeAngle(angle1-angle); if(fabsf(diff)>1.5f) { - *aim_position = QuadGraph::get()->getNode(target_sector) + *aim_position = DriveGraph::get()->getNode(target_sector) ->getCenter(); return; } //direction is a vector from our kart to the sectors we are testing - direction = QuadGraph::get()->getNode(target_sector)->getCenter() + direction = DriveGraph::get()->getNode(target_sector)->getCenter() - m_kart->getXYZ(); float len=direction.length(); @@ -2159,16 +2159,16 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) { step_coord = m_kart->getXYZ()+direction*m_kart_length * float(i); - QuadGraph::get()->spatialToTrack(&step_track_coord, step_coord, + DriveGraph::get()->spatialToTrack(&step_track_coord, step_coord, *last_node ); float distance = fabsf(step_track_coord[0]); //If we are outside, the previous node is what we are looking for if ( distance + m_kart_width * 0.5f - > QuadGraph::get()->getNode(*last_node)->getPathWidth() ) + > DriveGraph::get()->getNode(*last_node)->getPathWidth() ) { - *aim_position = QuadGraph::get()->getNode(*last_node) + *aim_position = DriveGraph::get()->getNode(*last_node) ->getCenter(); return; } @@ -2176,7 +2176,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) angle = angle1; *last_node = target_sector; } // for i<100 - *aim_position = QuadGraph::get()->getNode(*last_node)->getCenter(); + *aim_position = DriveGraph::get()->getNode(*last_node)->getCenter(); } // findNonCrashingPoint //----------------------------------------------------------------------------- @@ -2185,7 +2185,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) */ void SkiddingAI::determineTrackDirection() { - const QuadGraph *qg = QuadGraph::get(); + const DriveGraph *qg = DriveGraph::get(); unsigned int succ = m_successor_index[m_track_node]; unsigned int next = qg->getNode(m_track_node)->getSuccessor(succ); @@ -2214,7 +2214,7 @@ void SkiddingAI::determineTrackDirection() // quicker be aligned with the track again). if(fabsf(angle_to_track) > 0.22222f * M_PI) { - m_current_track_direction = GraphNode::DIR_UNDEFINED; + m_current_track_direction = DriveNode::DIR_UNDEFINED; return; } @@ -2230,8 +2230,8 @@ void SkiddingAI::determineTrackDirection() } #endif - if(m_current_track_direction==GraphNode::DIR_LEFT || - m_current_track_direction==GraphNode::DIR_RIGHT ) + if(m_current_track_direction==DriveNode::DIR_LEFT || + m_current_track_direction==DriveNode::DIR_RIGHT ) { handleCurve(); } // if(m_current_track_direction == DIR_LEFT || DIR_RIGHT ) @@ -2256,7 +2256,7 @@ void SkiddingAI::handleCurve() // kart will already point towards the direction of the circle), and // the case that the kart is facing wrong was already tested for before - const QuadGraph *qg = QuadGraph::get(); + const DriveGraph *qg = DriveGraph::get(); Vec3 xyz = m_kart->getXYZ(); Vec3 tangent = m_kart->getTrans()(Vec3(0,0,1)) - xyz; Vec3 last_xyz = qg->getNode(m_last_direction_node)->getCenter(); @@ -2276,7 +2276,7 @@ void SkiddingAI::handleCurve() { i = m_next_node_index[i]; // Pick either the lower left or right point: - int index = m_current_track_direction==GraphNode::DIR_LEFT + int index = m_current_track_direction==DriveNode::DIR_LEFT ? 0 : 1; float r = (m_curve_center - *(qg->getNode(i))[index]).length(); if(m_current_curve_radius < r) @@ -2335,8 +2335,8 @@ bool SkiddingAI::canSkid(float steer_fraction) } // No skidding on straights - if(m_current_track_direction==GraphNode::DIR_STRAIGHT || - m_current_track_direction==GraphNode::DIR_UNDEFINED ) + if(m_current_track_direction==DriveNode::DIR_STRAIGHT || + m_current_track_direction==DriveNode::DIR_UNDEFINED ) { #ifdef DEBUG if(m_controls->m_skid && m_ai_debug) @@ -2350,7 +2350,7 @@ bool SkiddingAI::canSkid(float steer_fraction) } const float MIN_SKID_SPEED = 5.0f; - const QuadGraph *qg = QuadGraph::get(); + const DriveGraph *qg = DriveGraph::get(); Vec3 last_xyz = qg->getNode(m_last_direction_node)->getCenter(); // Only try skidding when a certain minimum speed is reached. @@ -2361,7 +2361,7 @@ bool SkiddingAI::canSkid(float steer_fraction) Vec3 diff_last = last_xyz - m_curve_center; float angle_kart = atan2(diff_kart.getX(), diff_kart.getZ()); float angle_last = atan2(diff_last.getX(), diff_last.getZ()); - float angle = m_current_track_direction == GraphNode::DIR_RIGHT + float angle = m_current_track_direction == DriveNode::DIR_RIGHT ? angle_last - angle_kart : angle_kart - angle_last; angle = normalizeAngle(angle); @@ -2388,9 +2388,9 @@ bool SkiddingAI::canSkid(float steer_fraction) // left turn steer right to avoid getting too close to the left // vorder). In this case skidding will be useless. else if( (steer_fraction > 0 && - m_current_track_direction==GraphNode::DIR_LEFT) || + m_current_track_direction==DriveNode::DIR_LEFT) || (steer_fraction < 0 && - m_current_track_direction==GraphNode::DIR_RIGHT) ) + m_current_track_direction==DriveNode::DIR_RIGHT) ) { #ifdef DEBUG if(m_controls->m_skid && m_ai_debug) diff --git a/src/karts/controller/test_ai.hpp b/src/karts/controller/test_ai.hpp index ff7f73f32..e1f968c48 100644 --- a/src/karts/controller/test_ai.hpp +++ b/src/karts/controller/test_ai.hpp @@ -46,7 +46,7 @@ #include "karts/controller/ai_base_lap_controller.hpp" #include "race/race_manager.hpp" -#include "tracks/graph_node.hpp" +#include "tracks/drive_node.hpp" #include "utils/random_generator.hpp" #ifdef AI_DEBUG @@ -111,7 +111,7 @@ private: int m_start_kart_crash_direction; /** The direction of the track where the kart is on atm. */ - GraphNode::DirectionType m_current_track_direction; + DriveNode::DirectionType m_current_track_direction; /** The radius of the curve the kart is currently driving. Undefined * when being on a straigt section. */ diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index e5c752ad4..ae049374e 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -64,8 +64,8 @@ #include "physics/physics.hpp" #include "race/history.hpp" #include "tracks/terrain_info.hpp" -#include "tracks/quad_graph.hpp" -#include "tracks/graph_node.hpp" +#include "tracks/drive_graph.hpp" +#include "tracks/drive_node.hpp" #include "tracks/track.hpp" #include "tracks/track_manager.hpp" #include "utils/constants.hpp" @@ -1296,14 +1296,14 @@ void Kart::update(float dt) // To be used later float dist_to_sector = 0.0f; - if (QuadGraph::get()) + if (DriveGraph::get()) { const int sector = ((LinearWorld*)World::getWorld()) ->getTrackSector(getWorldKartId()).getCurrentGraphNode(); dist_to_sector = getXYZ().distance - (QuadGraph::get()->getNode(sector)->getCenter()); + (DriveGraph::get()->getNode(sector)->getCenter()); - const Vec3& quad_normal = QuadGraph::get()->getNode(sector) + const Vec3& quad_normal = DriveGraph::get()->getNode(sector) ->getNormal(); const btQuaternion& q = getTrans().getRotation(); const float roll = quad_normal.angle @@ -1941,13 +1941,13 @@ void Kart::crashed(const Material *m, const Vec3 &normal) World::getWorld()->getTrack()->isPushBackEnabled()) { int sector = lw->getSectorForKart(this); - if(sector!=QuadGraph::UNKNOWN_SECTOR) + if(sector!=Graph::UNKNOWN_SECTOR) { // Use the first predecessor node, which is the most // natural one (i.e. the one on the main driveline). - const GraphNode* gn = QuadGraph::get()->getNode( - QuadGraph::get()->getNode(sector)->getPredecessor(0)); - Vec3 impulse = gn->getCenter() - getXYZ(); + const DriveNode* dn = DriveGraph::get()->getNode( + DriveGraph::get()->getNode(sector)->getPredecessor(0)); + Vec3 impulse = dn->getCenter() - getXYZ(); impulse.setY(0); if(impulse.getX() || impulse.getZ()) impulse.normalize(); diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index 678bf3bc2..9846702a7 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -30,8 +30,8 @@ #include "physics/physics.hpp" #include "race/history.hpp" #include "states_screens/race_gui_base.hpp" -#include "tracks/graph_node.hpp" -#include "tracks/quad_graph.hpp" +#include "tracks/drive_graph.hpp" +#include "tracks/drive_node.hpp" #include "tracks/track_sector.hpp" #include "tracks/track.hpp" #include "utils/constants.hpp" @@ -393,7 +393,7 @@ void LinearWorld::newLap(unsigned int kart_index) int LinearWorld::getSectorForKart(const AbstractKart *kart) const { if(kart->getWorldKartId()>=m_kart_info.size()) - return QuadGraph::UNKNOWN_SECTOR; + return Graph::UNKNOWN_SECTOR; return m_kart_info[kart->getWorldKartId()].getTrackSector() ->getCurrentGraphNode(); } // getSectorForKart @@ -629,7 +629,7 @@ float LinearWorld::estimateFinishTimeForKart(AbstractKart* kart) */ unsigned int LinearWorld::getNumberOfRescuePositions() const { - return QuadGraph::get()->getNumNodes(); + return DriveGraph::get()->getNumNodes(); } // getNumberOfRescuePositions // ------------------------------------------------------------------------ @@ -647,8 +647,8 @@ unsigned int LinearWorld::getRescuePositionIndex(AbstractKart *kart) // ------------------------------------------------------------------------ btTransform LinearWorld::getRescueTransform(unsigned int index) const { - const Vec3 &xyz = QuadGraph::get()->getNode(index)->getCenter(); - const Vec3 &normal = QuadGraph::get()->getNode(index)->getNormal(); + const Vec3 &xyz = DriveGraph::get()->getNode(index)->getCenter(); + const Vec3 &normal = DriveGraph::get()->getNode(index)->getNormal(); btTransform pos; pos.setOrigin(xyz); @@ -866,11 +866,11 @@ void LinearWorld::checkForWrongDirection(unsigned int i, float dt) // will be one direction in which it isn't going backwards anyway. int sector = m_kart_info[i].getTrackSector()->getCurrentGraphNode(); - if (QuadGraph::get()->getNumberOfSuccessors(sector) > 1) + if (DriveGraph::get()->getNumberOfSuccessors(sector) > 1) return; // check if the player is going in the wrong direction - const GraphNode* node = QuadGraph::get()->getNode(sector); + const DriveNode* node = DriveGraph::get()->getNode(sector); Vec3 center_line = node->getUpperCenter() - node->getLowerCenter(); float angle_diff = kart->getVelocity().angle(center_line); diff --git a/src/tracks/arena_graph.cpp b/src/tracks/arena_graph.cpp index 3691d5c0b..d27079bb4 100644 --- a/src/tracks/arena_graph.cpp +++ b/src/tracks/arena_graph.cpp @@ -49,8 +49,9 @@ ArenaGraph::ArenaGraph(const std::string &navmesh, const XMLNode *node) // ----------------------------------------------------------------------------- ArenaNode* ArenaGraph::getNode(unsigned int i) const { + assert(i < m_all_nodes.size()); ArenaNode* n = dynamic_cast(m_all_nodes[i]); - assert(n!= NULL); + assert(n != NULL); return n; } // getNode diff --git a/src/tracks/arena_graph.hpp b/src/tracks/arena_graph.hpp index 59a8c0e41..1397265fe 100644 --- a/src/tracks/arena_graph.hpp +++ b/src/tracks/arena_graph.hpp @@ -28,6 +28,7 @@ class ArenaNode; class XMLNode; /** + * \brief A graph made from navmesh * \ingroup tracks */ class ArenaGraph : public Graph diff --git a/src/tracks/check_manager.cpp b/src/tracks/check_manager.cpp index 3f9b4f4fa..8f4788162 100644 --- a/src/tracks/check_manager.cpp +++ b/src/tracks/check_manager.cpp @@ -28,7 +28,7 @@ #include "tracks/check_lap.hpp" #include "tracks/check_line.hpp" #include "tracks/check_structure.hpp" -#include "tracks/quad_graph.hpp" +#include "tracks/drive_graph.hpp" #include "utils/log.hpp" CheckManager *CheckManager::m_check_manager = NULL; @@ -85,7 +85,7 @@ void CheckManager::load(const XMLNode &node) for(it=check_structures_to_change_state.begin(); it != check_structures_to_change_state.end(); it++) { - if(QuadGraph::get()->isReverse()) + if(DriveGraph::get()->isReverse()) m_all_checks[*it]->addSuccessor(i); else m_all_checks[i]->addSuccessor(*it); diff --git a/src/tracks/quad_graph.cpp b/src/tracks/drive_graph.cpp similarity index 57% rename from src/tracks/quad_graph.cpp rename to src/tracks/drive_graph.cpp index d712c0ab9..3c86963f9 100644 --- a/src/tracks/quad_graph.cpp +++ b/src/tracks/drive_graph.cpp @@ -16,64 +16,17 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, B -#include "tracks/quad_graph.hpp" +#include "tracks/drive_graph.hpp" -#include "LinearMath/btTransform.h" - -#include -#include -#include "graphics/central_settings.hpp" #include "config/user_config.hpp" -#include "graphics/callbacks.hpp" -#include "graphics/irr_driver.hpp" -#include "graphics/screen_quad.hpp" -#include "graphics/shaders.hpp" -#include "graphics/rtts.hpp" #include "io/file_manager.hpp" #include "io/xml_node.hpp" #include "modes/world.hpp" #include "tracks/check_lap.hpp" #include "tracks/check_line.hpp" #include "tracks/check_manager.hpp" -#include "tracks/node_2d.hpp" -#include "tracks/node_3d.hpp" +#include "tracks/drive_node.hpp" #include "tracks/track.hpp" -#include "graphics/glwrap.hpp" - -const int QuadGraph::UNKNOWN_SECTOR = -1; -QuadGraph *QuadGraph::m_quad_graph = NULL; - -/** Factory method to dynamic create 2d / 3d node */ -GraphNode* createNode(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, - const Vec3 &p3, unsigned int node_index, - bool invisible, bool ai_ignore) -{ - // Find the normal of this node by computing the normal of two triangles - // and taking their average. - core::triangle3df tri1(p0.toIrrVector(), p1.toIrrVector(), - p2.toIrrVector()); - core::triangle3df tri2(p0.toIrrVector(), p2.toIrrVector(), - p3.toIrrVector()); - Vec3 normal1 = tri1.getNormal(); - Vec3 normal2 = tri2.getNormal(); - Vec3 normal = -0.5f * (normal1 + normal2); - normal.normalize(); - - // Use the angle between the normal and an up vector to choose 3d/2d node - const float angle = normal.angle(Vec3(0, 1, 0)); - if (angle > 0.5f) - { - Log::debug("TrackNode", "3d node created, normal: %f, %f, %f", - normal.x(), normal.y(), normal.z()); - return new Node3D(p0, p1, p2, p3, normal, node_index, invisible, - ai_ignore); - } - - Log::debug("TrackNode", "2d node created, normal: %f, %f, %f", - normal.x(), normal.y(), normal.z()); - return new Node2D(p0, p1, p2, p3, normal, node_index, invisible, - ai_ignore); -} // createNode // ---------------------------------------------------------------------------- /** Constructor, loads the graph information for a given set of quads @@ -81,36 +34,23 @@ GraphNode* createNode(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, * \param quad_file_name Name of the file of all quads * \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 bool reverse) : m_reverse(reverse) +DriveGraph::DriveGraph(const std::string &quad_file_name, + const std::string &graph_file_name, + const bool reverse) : m_reverse(reverse) { - m_lap_length = 0; - m_quad_filename = quad_file_name; - m_quad_graph = this; + m_lap_length = 0; + m_quad_filename = quad_file_name; + Graph::setGraph(this); load(quad_file_name, graph_file_name); -} // QuadGraph +} // DriveGraph // ---------------------------------------------------------------------------- -/** Destructor, removes all nodes of the graph. */ -QuadGraph::~QuadGraph() -{ - for(unsigned int i=0; iaddSuccessor(from); + getNode(to)->addSuccessor(from); else - m_all_nodes[from]->addSuccessor(to); + getNode(from)->addSuccessor(to); } // addSuccessor @@ -120,8 +60,9 @@ void QuadGraph::addSuccessor(unsigned int from, unsigned int to) p1="n:p" : get point p from square n (n, p integers) p1="p1,p2,p3" : make a 3d point out of these 3 floating point values */ -void QuadGraph::getPoint(const XMLNode *xml, const std::string &attribute_name, - Vec3* result) const +void DriveGraph::getPoint(const XMLNode *xml, + const std::string &attribute_name, + Vec3* result) const { std::string s; xml->get(attribute_name, &s); @@ -141,30 +82,28 @@ void QuadGraph::getPoint(const XMLNode *xml, const std::string &attribute_name, } // getPoint // ---------------------------------------------------------------------------- -/** Loads a quad graph from a file. +/** Loads a drive graph from a file. * \param filename Name of the quad file to load. * \param filename Name of the graph file to load. */ -void QuadGraph::load(const std::string &quad_file_name, - const std::string &filename) +void DriveGraph::load(const std::string &quad_file_name, + const std::string &filename) { XMLNode *quad = file_manager->createXMLTree(quad_file_name); if (!quad || quad->getName() != "quads") { - Log::error("Quad Graph : Quad xml '%s' not found.", filename.c_str()); + Log::error("DriveGraph : Quad xml '%s' not found.", filename.c_str()); delete quad; return; } - m_min = Vec3( 99999, 99999, 99999); - m_max = Vec3(-99999, -99999, -99999); // Each quad is part of the graph exactly once now. for(unsigned int i=0; igetNumNodes(); i++) { const XMLNode *xml_node = quad->getNode(i); if(xml_node->getName()!="quad") { - Log::warn("Quad Graph: Unsupported node type '%s' found in '%s' - ignored.", + Log::warn("DriveGraph: Unsupported node type '%s' found in '%s' - ignored.", xml_node->getName().c_str(), filename.c_str()); continue; } @@ -182,11 +121,10 @@ void QuadGraph::load(const std::string &quad_file_name, xml_node->get("invisible", &invisible); bool ai_ignore=false; xml_node->get("ai-ignore", &ai_ignore); - GraphNode* node = - createNode(p0, p1, p2, p3, m_all_nodes.size(), invisible, ai_ignore); - m_max.max(p0);m_max.max(p1);m_max.max(p2);m_max.max(p3); - m_min.min(p0);m_min.min(p1);m_min.min(p2);m_min.min(p3); - m_all_nodes.push_back(node); + createQuad(p0, p1, p2, p3, m_all_nodes.size(), invisible, ai_ignore, + false/*is_arena*/); + m_bb_max.max(p0);m_bb_max.max(p1);m_bb_max.max(p2);m_bb_max.max(p3); + m_bb_min.min(p0);m_bb_min.min(p1);m_bb_min.min(p2);m_bb_min.min(p3); } delete quad; @@ -201,12 +139,12 @@ void QuadGraph::load(const std::string &quad_file_name, if (m_all_nodes.size() > 0) { - m_lap_length = m_all_nodes[m_all_nodes.size()-1]->getDistanceFromStart() - + m_all_nodes[m_all_nodes.size()-1]->getDistanceToSuccessor(0); + m_lap_length = getNode(m_all_nodes.size()-1)->getDistanceFromStart() + + getNode(m_all_nodes.size()-1)->getDistanceToSuccessor(0); } else { - Log::error("Quad Graph", "No node in driveline graph."); + Log::error("DriveGraph", "No node in driveline graph."); m_lap_length = 10.0f; } @@ -265,7 +203,7 @@ void QuadGraph::load(const std::string &quad_file_name, } // edge else { - Log::error("Quad Graph", "Incorrect specification in '%s': '%s' ignored.", + Log::error("DriveGraph", "Incorrect specification in '%s': '%s' ignored.", filename.c_str(), xml_node->getName().c_str()); continue; } // incorrect specification @@ -281,8 +219,8 @@ void QuadGraph::load(const std::string &quad_file_name, m_lap_length = -1; for(unsigned int i=0; igetDistanceFromStart() - + m_all_nodes[i]->getDistanceToSuccessor(0); + float l = getNode(i)->getDistanceFromStart() + + getNode(i)->getDistanceToSuccessor(0); if(l > m_lap_length) m_lap_length = l; } @@ -296,18 +234,18 @@ void QuadGraph::load(const std::string &quad_file_name, * but in reverse mode (where node 0 is actually the end of the track) * this is 0's successor. */ -unsigned int QuadGraph::getStartNode() const +unsigned int DriveGraph::getStartNode() const { - return m_reverse ? m_all_nodes[0]->getSuccessor(0) + return m_reverse ? getNode(0)->getSuccessor(0) : 0; } // getStartNode // ---------------------------------------------------------------------------- /** Sets the checkline requirements for all nodes in the graph. */ -void QuadGraph::computeChecklineRequirements() +void DriveGraph::computeChecklineRequirements() { - computeChecklineRequirements(m_all_nodes[0], + computeChecklineRequirements(getNode(0), CheckManager::get()->getLapLineIndex()); } // computeChecklineRequirements @@ -315,8 +253,8 @@ void QuadGraph::computeChecklineRequirements() /** Finds which checklines must be visited before driving on this quad * (useful for rescue) */ -void QuadGraph::computeChecklineRequirements(GraphNode* node, - int latest_checkline) +void DriveGraph::computeChecklineRequirements(DriveNode* node, + int latest_checkline) { for (unsigned int n=0; ngetNumberOfSuccessors(); n++) { @@ -325,7 +263,7 @@ void QuadGraph::computeChecklineRequirements(GraphNode* node, // warp-around if (succ_id == 0) break; - GraphNode* succ = m_all_nodes[succ_id]; + DriveNode* succ = getNode(succ_id); int new_latest_checkline = CheckManager::get()->getChecklineTriggering(node->getCenter(), succ->getCenter() ); @@ -363,11 +301,11 @@ void QuadGraph::computeChecklineRequirements(GraphNode* node, * (since on other graph nodes only one path can be used anyway, this * saves some memory). */ -void QuadGraph::setupPaths() +void DriveGraph::setupPaths() { for(unsigned int i=0; isetupPathsToNode(); + getNode(i)->setupPathsToNode(); } } // setupPaths @@ -375,18 +313,18 @@ void QuadGraph::setupPaths() /** This function sets a default successor for all graph nodes that currently * don't have a successor defined. The default successor of node X is X+1. */ -void QuadGraph::setDefaultSuccessors() +void DriveGraph::setDefaultSuccessors() { for(unsigned int i=0; igetNumberOfSuccessors()==0) { + if(getNode(i)->getNumberOfSuccessors()==0) { 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); + //~ getNode(i)->addSuccessor(i+1>=m_all_nodes.size() ? 0 : i+1); } // if size==0 } // for i +void DriveGraph::setDefaultStartPositions(AlignedArray *start_transforms, - unsigned int karts_per_row, - float forwards_distance, - float sidewards_distance, - float upwards_distance) const + unsigned int karts_per_row, + float forwards_distance, + float sidewards_distance, + float upwards_distance) const { // We start just before the start node (which will trigger lap // counting when reached). The first predecessor is the one on // the main driveline. - int current_node = m_all_nodes[getStartNode()]->getPredecessor(0); + int current_node = getNode(getStartNode())->getPredecessor(0); float distance_from_start = 0.1f+forwards_distance; @@ -443,14 +381,14 @@ void QuadGraph::setDefaultStartPositions(AlignedArray // Only follow the main driveline, i.e. first predecessor current_node = getNode(current_node)->getPredecessor(0); } - const GraphNode* gn = getNode(current_node); - Vec3 center_line = gn->getLowerCenter() - gn->getUpperCenter(); + const DriveNode* dn = getNode(current_node); + Vec3 center_line = dn->getLowerCenter() - dn->getUpperCenter(); center_line.normalize(); - Vec3 horizontal_line = (*gn)[2] - (*gn)[3]; + Vec3 horizontal_line = (*dn)[2] - (*dn)[3]; horizontal_line.normalize(); - Vec3 start = gn->getUpperCenter() + Vec3 start = dn->getUpperCenter() + center_line * distance_from_start + horizontal_line * x_pos; // Add a certain epsilon to the height in case that the @@ -458,7 +396,7 @@ void QuadGraph::setDefaultStartPositions(AlignedArray (*start_transforms)[i].setOrigin(start+Vec3(0,upwards_distance,0)); (*start_transforms)[i].setRotation( btQuaternion(btVector3(0, 1, 0), - gn->getAngleToSuccessor(0))); + dn->getAngleToSuccessor(0))); if(x_pos >= max_x_dist-sidewards_distance*0.5f) { x_pos = -max_x_dist; @@ -482,17 +420,17 @@ void QuadGraph::setDefaultStartPositions(AlignedArray * \param succ A vector of ints to which the successors are added. * \param for_ai true if only quads accessible by the AI should be returned. */ -void QuadGraph::getSuccessors(int node_number, - std::vector& succ, - bool for_ai) const +void DriveGraph::getSuccessors(int node_number, + std::vector& succ, + bool for_ai) const { - const GraphNode *gn=m_all_nodes[node_number]; - for(unsigned int i=0; igetNumberOfSuccessors(); i++) + const DriveNode *dn=getNode(node_number); + for(unsigned int i=0; igetNumberOfSuccessors(); i++) { // If getSuccessor is called for the AI, only add // quads that are meant for the AI to be used. - if(!for_ai || !gn->ignoreSuccessorForAI(i)) - succ.push_back(gn->getSuccessor(i)); + if(!for_ai || !dn->ignoreSuccessorForAI(i)) + succ.push_back(dn->getSuccessor(i)); } } // getSuccessors @@ -502,10 +440,10 @@ void QuadGraph::getSuccessors(int node_number, * \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) +void DriveGraph::computeDistanceFromStart(unsigned int node, float new_distance) { - GraphNode *gn = m_all_nodes[node]; - float current_distance = gn->getDistanceFromStart(); + DriveNode *dn = getNode(node); + float current_distance = dn->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 @@ -519,25 +457,25 @@ void QuadGraph::computeDistanceFromStart(unsigned int node, float new_distance) if(current_distancegetNodeIndex(), delta, 0); + updateDistancesForAllSuccessors(dn->getIndex(), delta, 0); } return; } // Otherwise this node has no distance defined yet. Set the new // distance, and recursively update all following nodes. - gn->setDistanceFromStart(new_distance); + dn->setDistanceFromStart(new_distance); - for(unsigned int i=0; igetNumberOfSuccessors(); i++) + for(unsigned int i=0; igetNumberOfSuccessors(); i++) { - GraphNode *gn_next = m_all_nodes[gn->getSuccessor(i)]; + DriveNode *dn_next = getNode(dn->getSuccessor(i)); // The start node (only node with distance 0) is reached again, // recursion can stop now - if(gn_next->getDistanceFromStart()==0) + if(dn_next->getDistanceFromStart()==0) continue; - computeDistanceFromStart(gn_next->getNodeIndex(), - new_distance + gn->getDistanceToSuccessor(i)); + computeDistanceFromStart(dn_next->getIndex(), + new_distance + dn->getDistanceToSuccessor(i)); } // for i } // computeDistanceFromStart @@ -551,35 +489,35 @@ void QuadGraph::computeDistanceFromStart(unsigned int node, float new_distance) * \param recursive_count Counts how often this function was called * recursively in order to catch incorrect graphs that contain loops. */ -void QuadGraph::updateDistancesForAllSuccessors(unsigned int indx, float delta, +void DriveGraph::updateDistancesForAllSuccessors(unsigned int indx, float delta, unsigned int recursive_count) { if(recursive_count>getNumNodes()) { - Log::error("QuadGraph", - "Quad graph contains a loop (without start node)."); - Log::fatal("QuadGraph", + Log::error("DriveGraph", + "DriveGraph contains a loop (without start node)."); + Log::fatal("DriveGraph", "Fix graph, check for directions of all shortcuts etc."); } recursive_count++; - GraphNode* g = getNode(indx); - g->setDistanceFromStart(g->getDistanceFromStart()+delta); - for(unsigned int i=0; igetNumberOfSuccessors(); i++) + DriveNode* dn = getNode(indx); + dn->setDistanceFromStart(dn->getDistanceFromStart()+delta); + for(unsigned int i=0; igetNumberOfSuccessors(); i++) { - GraphNode* g_next = getNode(g->getSuccessor(i)); + DriveNode* dn_next = getNode(dn->getSuccessor(i)); // Stop when we reach the start node, i.e. the only node with a // distance of 0 - if(g_next->getDistanceFromStart()==0) + if(dn_next->getDistanceFromStart()==0) continue; // Only increase the distance from start of a successor node, if // this successor has a distance from start that is smaller then // the increased amount. - if(g->getDistanceFromStart()+g->getDistanceToSuccessor(i) > - g_next->getDistanceFromStart()) + if(dn->getDistanceFromStart()+dn->getDistanceToSuccessor(i) > + dn_next->getDistanceFromStart()) { - updateDistancesForAllSuccessors(g->getSuccessor(i), delta, + updateDistancesForAllSuccessors(dn->getSuccessor(i), delta, recursive_count); } } @@ -600,12 +538,12 @@ void QuadGraph::updateDistancesForAllSuccessors(unsigned int indx, float delta, * its data constantly, i.e. if it takes a different turn, it will be using * the new data). */ -void QuadGraph::computeDirectionData() +void DriveGraph::computeDirectionData() { for(unsigned int i=0; igetNumberOfSuccessors(); + succ_indexgetNumberOfSuccessors(); succ_index++) { determineDirection(i, succ_index); @@ -617,14 +555,14 @@ void QuadGraph::computeDirectionData() //----------------------------------------------------------------------------- /** Adjust the given angle to be in [-PI, PI]. */ -float QuadGraph::normalizeAngle(float f) +float DriveGraph::normalizeAngle(float f) { if(f>M_PI) f -= 2*M_PI; else if(f<-M_PI) f += 2*M_PI; return f; } // normalizeAngle //----------------------------------------------------------------------------- -/** Determines the direction of the quad graph when driving to the specified +/** Determines the direction of the drive graph when driving to the specified * successor. It also determines the last graph node that is still following * the given direction. The computed data is saved in the corresponding * graph node. @@ -640,8 +578,8 @@ float QuadGraph::normalizeAngle(float f) * If there should be any other branches later, successor * 0 will always be tetsed. */ -void QuadGraph::determineDirection(unsigned int current, - unsigned int succ_index) +void DriveGraph::determineDirection(unsigned int current, + unsigned int succ_index) { // The maximum angle which is still considered to be straight const float max_straight_angle=0.1f; @@ -681,11 +619,11 @@ void QuadGraph::determineDirection(unsigned int current, next = getNode(next)->getSuccessor(0); } // while(1) - GraphNode::DirectionType dir = - rel_angle==0 ? GraphNode::DIR_STRAIGHT - : (rel_angle>0) ? GraphNode::DIR_RIGHT - : GraphNode::DIR_LEFT; - m_all_nodes[current]->setDirectionData(succ_index, dir, next); + DriveNode::DirectionType dir = + rel_angle==0 ? DriveNode::DIR_STRAIGHT + : (rel_angle>0) ? DriveNode::DIR_RIGHT + : DriveNode::DIR_LEFT; + getNode(current)->setDirectionData(succ_index, dir, next); } // determineDirection @@ -699,12 +637,12 @@ void QuadGraph::determineDirection(unsigned int current, * \param xyz The position of the kart. * \param sector The graph node the position is on. */ -void QuadGraph::spatialToTrack(Vec3 *dst, const Vec3& xyz, +void DriveGraph::spatialToTrack(Vec3 *dst, const Vec3& xyz, const int sector) const { if(sector == UNKNOWN_SECTOR ) { - Log::warn("Quad Graph", "UNKNOWN_SECTOR in spatialToTrack()."); + Log::warn("Drive Graph", "UNKNOWN_SECTOR in spatialToTrack()."); return; } @@ -712,215 +650,47 @@ void QuadGraph::spatialToTrack(Vec3 *dst, const Vec3& xyz, } // spatialToTrack //----------------------------------------------------------------------------- -/** findRoadSector returns in which sector on the road the position - * xyz is. If xyz is not on top of the road, it sets UNKNOWN_SECTOR as sector. - * - * \param xyz Position for which the segment should be determined. - * \param sector Contains the previous sector (as a shortcut, since usually - * the sector is the same as the last one), and on return the result - * \param all_sectors If this is not NULL, it is a list of all sectors to - * test. This is used by the AI to make sure that it ends up on the - * selected way in case of a branch, and also to make sure that it - * doesn't skip e.g. a loop (see explanation below for details). - */ -void QuadGraph::findRoadSector(const Vec3& xyz, int *sector, - std::vector *all_sectors) const +float DriveGraph::getDistanceToNext(int n, int j) const { - // Most likely the kart will still be on the sector it was before, - // so this simple case is tested first. - if(*sector!=UNKNOWN_SECTOR && getNode(*sector)->pointInside(xyz) ) - { - return; - } // if still on same quad - - // Now we search through all graph nodes, starting with - // the current one - int indx = *sector; - - // If a current sector is given, and max_lookahead is specify, only test - // the next max_lookahead graph nodes instead of testing the whole graph. - // This is necessary for the AI: if the track contains a loop, e.g.: - // -A--+---B---+----F-------- - // E C - // +---D---+ - // and the track is supposed to be driven: ABCDEBF, the AI might find - // the node on F, and then keep on going straight ahead instead of - // using the loop at all. - unsigned int max_count = (*sector!=UNKNOWN_SECTOR && all_sectors!=NULL) - ? (unsigned int)all_sectors->size() - : (unsigned int)m_all_nodes.size(); - *sector = UNKNOWN_SECTOR; - for(unsigned int i=0; ipointInside(xyz)) - { - *sector = indx; - } - } // for i *all_sectors) const -{ - int count = (all_sectors!=NULL) ? (int) all_sectors->size() : getNumNodes(); - int current_sector = 0; - if(curr_sector != UNKNOWN_SECTOR && !all_sectors) - { - // We have to test all nodes here: reason is that on track with - // shortcuts the n quads of the main drivelines is followed by - // the quads of the shortcuts. So after quad n-1 (the last one - // before the lap counting line) quad n will not be 0 (the first - // quad after the lap counting line), but one of the quads on a - // shortcut. If we only tested a limited number of quads to - // improve the performance the crossing of a lap might not be - // detected (because quad 0 is not tested, only quads on the - // shortcuts are tested). If this should become a performance - // bottleneck, we need to set up a graph of 'next' quads for each - // quad (similar to what the AI does), and only test the quads - // in this graph. - const int LIMIT = getNumNodes(); - count = LIMIT; - // Start 10 quads before the current quad, so the quads closest - // to the current position are tested first. - current_sector = curr_sector -10; - if(current_sector<0) current_sector += getNumNodes(); - } - - int min_sector = UNKNOWN_SECTOR; - float min_dist_2 = 999999.0f*999999.0f; - - // If a kart is falling and in between (or too far below) - // a driveline point it might not fulfill - // the height condition. So we run the test twice: first with height - // condition, then again without the height condition - just to make sure - // it always comes back with some kind of quad. - for(int phase=0; phase<2; phase++) - { - for(int j=0; jgetDistance2FromPoint(xyz); - if(dist_2(gn) != NULL); - float dist = xyz.getY() - gn->getMinHeight(); - // While negative distances are unlikely, we allow some small - // negative numbers in case that the kart is partly in the - // track. Only do the height test in phase==0, in phase==1 - // accept any point, independent of height, or this node is 3d - // which already takes height into account - if(phase==1 || (dist < 5.0f && dist>-1.0f) || is_3d) - { - min_dist_2 = dist_2; - min_sector = next_sector; - } - } - current_sector = next_sector; - } // for j - // Leave in phase 0 if any sector was found. - if(min_sector!=UNKNOWN_SECTOR) - return min_sector; - } // phase - - if(min_sector==UNKNOWN_SECTOR ) - { - Log::info("Quad Graph", "unknown sector found."); - } - return min_sector; -} // findOutOfRoadSector - -//----------------------------------------------------------------------------- -void QuadGraph::set3DVerticesOfGraph(int i, video::S3DVertex *v, - const video::SColor &color) const -{ - m_all_nodes[i]->getVertices(v, color); -} // set3DVerticesOfGraph - -//----------------------------------------------------------------------------- -const bool QuadGraph::isNodeInvisible(int n) const -{ - return m_all_nodes[n]->isInvisible(); -} // isNodeInvisible - -//----------------------------------------------------------------------------- -float QuadGraph::getDistanceToNext(int n, int j) const -{ - return m_all_nodes[n]->getDistanceToSuccessor(j); + return getNode(n)->getDistanceToSuccessor(j); } // getDistanceToNext //----------------------------------------------------------------------------- -float QuadGraph::getAngleToNext(int n, int j) const +float DriveGraph::getAngleToNext(int n, int j) const { - return m_all_nodes[n]->getAngleToSuccessor(j); + return getNode(n)->getAngleToSuccessor(j); } // getAngleToNext //----------------------------------------------------------------------------- -int QuadGraph::getNumberOfSuccessors(int n) const +int DriveGraph::getNumberOfSuccessors(int n) const { - return m_all_nodes[n]->getNumberOfSuccessors(); + return getNode(n)->getNumberOfSuccessors(); } // getNumberOfSuccessors //----------------------------------------------------------------------------- -float QuadGraph::getDistanceFromStart(int j) const +float DriveGraph::getDistanceFromStart(int j) const { - return m_all_nodes[j]->getDistanceFromStart(); + return getNode(j)->getDistanceFromStart(); } // getDistanceFromStart -//----------------------------------------------------------------------------- -const bool QuadGraph::differentNodeColor(int n, NodeColor* c) const +// ----------------------------------------------------------------------------- +void DriveGraph::differentNodeColor(int n, video::SColor* c) const { if (UserConfigParams::m_track_debug) { - if (dynamic_cast(m_all_nodes[n]) != NULL) - *c = COLOR_GREEN; + if (getNode(n)->is3DQuad()) + *c = video::SColor(255, 0, 255, 0); else - *c = COLOR_YELLOW; - return true; + *c = video::SColor(255, 255, 255, 0); } - return n == 0; + } // differentNodeColor + +// ----------------------------------------------------------------------------- +DriveNode* DriveGraph::getNode(unsigned int j) const +{ + assert(j < m_all_nodes.size()); + DriveNode* n = dynamic_cast(m_all_nodes[j]); + assert(n != NULL); + return n; +} // getNode diff --git a/src/tracks/drive_graph.hpp b/src/tracks/drive_graph.hpp new file mode 100644 index 000000000..c41b109dd --- /dev/null +++ b/src/tracks/drive_graph.hpp @@ -0,0 +1,125 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2009-2015 Joerg Henrichs +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, B + +#ifndef HEADER_DRIVE_GRAPH_HPP +#define HEADER_DRIVE_GRAPH_HPP + +#include +#include + +#include "tracks/graph.hpp" +#include "utils/aligned_array.hpp" +#include "utils/cpp2011.hpp" + +#include "LinearMath/btTransform.h" + +class DriveNode; +class XMLNode; + +/** + * \brief A graph made from driveline + * \ingroup tracks + */ +class DriveGraph : public Graph +{ +private: + /** The length of the first loop. */ + float m_lap_length; + + /** 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 computeChecklineRequirements(DriveNode* node, int latest_checkline); + // ------------------------------------------------------------------------ + void computeDirectionData(); + // ------------------------------------------------------------------------ + void determineDirection(unsigned int current, unsigned int succ_index); + // ------------------------------------------------------------------------ + float normalizeAngle(float f); + // ------------------------------------------------------------------------ + void addSuccessor(unsigned int from, unsigned int to); + // ------------------------------------------------------------------------ + void load(const std::string &quad_file_name, const std::string &filename); + // ------------------------------------------------------------------------ + void getPoint(const XMLNode *xml, const std::string &attribute_name, + Vec3 *result) const; + // ------------------------------------------------------------------------ + void computeDistanceFromStart(unsigned int start_node, float distance); + // ------------------------------------------------------------------------ + unsigned int getStartNode() const; + // ------------------------------------------------------------------------ + virtual bool hasLapLine() const OVERRIDE { return true; } + // ------------------------------------------------------------------------ + virtual void differentNodeColor(int n, video::SColor* c) const OVERRIDE; + +public: + static DriveGraph* get() { return dynamic_cast(m_graph); } + // ------------------------------------------------------------------------ + DriveGraph(const std::string &quad_file_name, + const std::string &graph_file_name, const bool reverse); + // ------------------------------------------------------------------------ + virtual ~DriveGraph() {} + // ------------------------------------------------------------------------ + void getSuccessors(int node_number, std::vector& succ, + bool for_ai=false) const; + // ------------------------------------------------------------------------ + void spatialToTrack(Vec3 *dst, const Vec3& xyz, const int sector) const; + // ------------------------------------------------------------------------ + void setDefaultStartPositions(AlignedArray *start_transforms, + unsigned int karts_per_row, + float forwards_distance = 1.5f, + float sidewards_distance = 1.5f, + float upwards_distance=0.0f) const; + // ------------------------------------------------------------------------ + void updateDistancesForAllSuccessors(unsigned int indx, float delta, + unsigned int count); + // ------------------------------------------------------------------------ + void setupPaths(); + // ------------------------------------------------------------------------ + void computeChecklineRequirements(); + // ------------------------------------------------------------------------ + /** Return the distance to the j-th successor of node n. */ + float getDistanceToNext(int n, int j) const; + // ------------------------------------------------------------------------ + /** Returns the angle of the line between node n and its j-th. + * successor. */ + float getAngleToNext(int n, int j) const; + // ------------------------------------------------------------------------ + /** Returns the number of successors of a node n. */ + int getNumberOfSuccessors(int n) const; + // ------------------------------------------------------------------------ + /** Returns the quad that belongs to a graph node. */ + DriveNode* getNode(unsigned int j) const; + // ------------------------------------------------------------------------ + /** Returns the distance from the start to the beginning of a quad. */ + float getDistanceFromStart(int j) const; + // ------------------------------------------------------------------------ + /** Returns the length of the main driveline. */ + float getLapLength() const { return m_lap_length; } + // ------------------------------------------------------------------------ + bool isReverse() const { return m_reverse; } + +}; // DriveGraph + +#endif diff --git a/src/tracks/graph_node.cpp b/src/tracks/drive_node.cpp similarity index 73% rename from src/tracks/graph_node.cpp rename to src/tracks/drive_node.cpp index e5d5efacd..eed79e85f 100644 --- a/src/tracks/graph_node.cpp +++ b/src/tracks/drive_node.cpp @@ -16,25 +16,22 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, B -#include "tracks/graph_node.hpp" +#include "tracks/drive_node.hpp" #include "io/file_manager.hpp" #include "io/xml_node.hpp" #include "matrix4.h" -#include "tracks/quad_graph.hpp" +#include "tracks/drive_graph.hpp" #include "utils/log.hpp" // ---------------------------------------------------------------------------- -GraphNode::GraphNode(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, +DriveNode::DriveNode(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, const Vec3 &normal, unsigned int node_index, bool invisible, bool ai_ignore) - :Quad(p0, p1, p2, p3) + :Quad(p0, p1, p2, p3, normal, node_index, invisible) { - m_invisible = invisible; m_ai_ignore = ai_ignore; - m_normal = normal; - m_node_index = node_index; m_distance_from_start = -1.0f; // The following values should depend on the actual orientation @@ -48,7 +45,7 @@ GraphNode::GraphNode(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, m_width = ( (m_p[1]-m_p[0]).length() + (m_p[3]-m_p[2]).length() ) * 0.5f; - if(QuadGraph::get()->isReverse()) + if(DriveGraph::get()->isReverse()) { m_lower_center = (m_p[2]+m_p[3]) * 0.5f; m_upper_center = (m_p[0]+m_p[1]) * 0.5f; @@ -60,53 +57,53 @@ GraphNode::GraphNode(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, m_upper_center = (m_p[2]+m_p[3]) * 0.5f; } -} // GraphNode +} // DriveNode // ---------------------------------------------------------------------------- /** Adds a successor to a node. This function will also pre-compute certain * values (like distance from this node to the successor, angle (in world) * between this node and the successor. - * \param to The index of the graph node of the successor. + * \param to The index of the drive node of the successor. */ -void GraphNode::addSuccessor(unsigned int to) +void DriveNode::addSuccessor(unsigned int to) { m_successor_nodes.push_back(to); - // to is the graph node - GraphNode* gn_to = QuadGraph::get()->getNode(to); + // to is the drive node + DriveNode* dn_to = DriveGraph::get()->getNode(to); - // Note that the first predecessor is (because of the way the quad graph + // Note that the first predecessor is (because of the way the drive graph // is exported) the most 'natural' one, i.e. the one on the main // driveline. - gn_to->m_predecessor_nodes.push_back(m_node_index); + dn_to->m_predecessor_nodes.push_back(m_index); - Vec3 d = m_lower_center - gn_to->m_lower_center; + Vec3 d = m_lower_center - dn_to->m_lower_center; m_distance_to_next.push_back(d.length()); - Vec3 diff = gn_to->getCenter() - getCenter(); + Vec3 diff = dn_to->getCenter() - getCenter(); core::CMatrix4 m; m.buildRotateFromTo(getNormal().toIrrVector(), Vec3(0, 1, 0).toIrrVector()); - core::vector3df diffRotated; - m.rotateVect(diffRotated, diff.toIrrVector()); + core::vector3df diff_rotated; + m.rotateVect(diff_rotated, diff.toIrrVector()); - m_angle_to_next.push_back(atan2(diffRotated.X, diffRotated.Z)); + m_angle_to_next.push_back(atan2(diff_rotated.X, diff_rotated.Z)); } // addSuccessor // ---------------------------------------------------------------------------- /** If this node has more than one successor, it will set up a vector that - * contains the direction to use when a certain graph node X should be + * contains the direction to use when a certain drive node X should be * reached. */ -void GraphNode::setupPathsToNode() +void DriveNode::setupPathsToNode() { if(m_successor_nodes.size()<2) return; - const unsigned int num_nodes = QuadGraph::get()->getNumNodes(); + const unsigned int num_nodes = DriveGraph::get()->getNumNodes(); m_path_to_node.resize(num_nodes); - // Initialise each graph node with -1, indicating that + // Initialise each drive node with -1, indicating that // it hasn't been reached yet. for(unsigned int i=0; igetNode(getSuccessor(i)); - gn->markAllSuccessorsToUse(i, &m_path_to_node); + DriveNode* dn = DriveGraph::get()->getNode(getSuccessor(i)); + dn->markAllSuccessorsToUse(i, &m_path_to_node); } #ifdef DEBUG for(unsigned int i = 0; i < m_path_to_node.size(); ++i) { if(m_path_to_node[i] == -1) - Log::warn("GraphNode", "No path to node %d found on graph node %d.", - i, m_node_index); + Log::warn("DriveNode", "No path to node %d found on drive node %d.", + i, m_index); } #endif } // setupPathsToNode @@ -145,22 +142,22 @@ void GraphNode::setupPathsToNode() * \param path_to_node The path-to-node data structure of the node for * which the paths are currently determined. */ -void GraphNode::markAllSuccessorsToUse(unsigned int n, +void DriveNode::markAllSuccessorsToUse(unsigned int n, PathToNodeVector *path_to_node) { // End recursion if the path to this node has already been found. - if( (*path_to_node)[m_node_index] >-1) return; + if( (*path_to_node)[m_index] >-1) return; - (*path_to_node)[m_node_index] = n; + (*path_to_node)[m_index] = n; for(unsigned int i=0; igetNode(getSuccessor(i)); - gn->markAllSuccessorsToUse(n, path_to_node); + DriveNode* dn = DriveGraph::get()->getNode(getSuccessor(i)); + dn->markAllSuccessorsToUse(n, path_to_node); } } // markAllSuccesorsToUse // ---------------------------------------------------------------------------- -void GraphNode::setDirectionData(unsigned int successor, DirectionType dir, +void DriveNode::setDirectionData(unsigned int successor, DirectionType dir, unsigned int last_node_index) { if(m_direction.size()getNode(m_successor_nodes[i])->letAIIgnore(); + return DriveGraph::get()->getNode(m_successor_nodes[i])->letAIIgnore(); } // ignoreSuccessorForAI diff --git a/src/tracks/graph_node.hpp b/src/tracks/drive_node.hpp similarity index 72% rename from src/tracks/graph_node.hpp rename to src/tracks/drive_node.hpp index 59e5ebac3..3497f64b3 100644 --- a/src/tracks/graph_node.hpp +++ b/src/tracks/drive_node.hpp @@ -16,19 +16,19 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, B -#ifndef HEADER_GRAPH_NODE_HPP -#define HEADER_GRAPH_NODE_HPP +#ifndef HEADER_DRIVE_NODE_HPP +#define HEADER_DRIVE_NODE_HPP #include #include "tracks/quad.hpp" /** - * \brief This class stores a node of the graph, i.e. a list of successor - * edges, it can either be 2d or 3d. + * \brief This class stores a node of the drive graph, i.e. a list of + * successor edges, it can either be 2d or 3d. * \ingroup tracks */ -class GraphNode : public Quad +class DriveNode : public Quad { public: /** To indiciate in which direction the track is going: @@ -37,29 +37,20 @@ public: enum DirectionType {DIR_STRAIGHT, DIR_LEFT, DIR_RIGHT, DIR_UNDEFINED}; protected: - /** Lower center point of the graph node. */ + /** Lower center point of the drive node. */ Vec3 m_lower_center; - /** Upper center point of the graph node. */ + /** Upper center point of the drive node. */ Vec3 m_upper_center; - /** Distance from the start to the beginning of the graph node. */ + /** Distance from the start to the beginning of the drive node. */ float m_distance_from_start; private: - /** Normal of the graph node */ - Vec3 m_normal; - - /** Set to true if this graph node should not be shown in the minimap. */ - bool m_invisible; - - /** Set to true if this graph node should not be used by the AI. */ + /** Set to true if this drive node should not be used by the AI. */ bool m_ai_ignore; - /** Index of this graph node. */ - unsigned int m_node_index; - - /** The list of successor graph nodes. */ + /** The list of successor drive nodes. */ std::vector m_successor_nodes; /** The list of predecessors of a node. */ @@ -79,18 +70,18 @@ private: Vec3 m_center_to_right; typedef std::vector PathToNodeVector; - /** This vector is only used if the graph node has more than one + /** This vector is only used if the drive node has more than one * successor. In this case m_path_to_node[X] will contain the index - * of the successor to use in order to reach graph node X for this - * graph nodes. */ + * of the successor to use in order to reach drive node X for this + * drive nodes. */ PathToNodeVector m_path_to_node; /** The direction for each of the successors. */ std::vector m_direction; - /** Stores for each successor the index of the last graph node that + /** Stores for each successor the index of the last drive node that * has the same direction (i.e. if index 0 curves left, this vector - * will store the index of the last graph node that is still turning + * will store the index of the last drive node that is still turning * left. */ std::vector m_last_index_same_direction; @@ -105,16 +96,17 @@ private: */ std::vector< int > m_checkline_requirements; + // ------------------------------------------------------------------------ void markAllSuccessorsToUse(unsigned int n, PathToNodeVector *m_path_to_node); public: - GraphNode(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, + DriveNode(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, const Vec3 &normal, unsigned int node_index, bool invisible, bool ai_ignore); // ------------------------------------------------------------------------ - virtual ~GraphNode() {} + virtual ~DriveNode() {} // ------------------------------------------------------------------------ void addSuccessor (unsigned int to); // ------------------------------------------------------------------------ @@ -131,7 +123,7 @@ public: // ------------------------------------------------------------------------ /** Returns the i-th successor node. */ unsigned int getSuccessor(unsigned int i) const - { return m_successor_nodes[i]; } + { return m_successor_nodes[i]; } // ------------------------------------------------------------------------ /** Returns the number of predecessors. */ unsigned int getNumberOfPredecessors() const @@ -140,47 +132,41 @@ public: /** Returns a predecessor for this node. Note that the first predecessor * is the most 'natural' one, i.e. the one on the main driveline. */ - int getPredecessor(unsigned int i) const {return m_predecessor_nodes[i]; } - // ------------------------------------------------------------------------ - /** Returns the node index of this node. */ - unsigned int getNodeIndex() const { return m_node_index; } - // ------------------------------------------------------------------------ - /** Returns the normal of this quad */ - const Vec3& getNormal() const { return m_normal; } + int getPredecessor(unsigned int i) const { return m_predecessor_nodes[i]; } // ------------------------------------------------------------------------ /** Returns the distance to the j-th. successor. */ float getDistanceToSuccessor(unsigned int j) const - { return m_distance_to_next[j]; } + { return m_distance_to_next[j]; } // ------------------------------------------------------------------------ /** Returns the angle from this node to the j-th. successor. */ float getAngleToSuccessor(unsigned int j) const - { return m_angle_to_next[j]; } + { return m_angle_to_next[j]; } // ------------------------------------------------------------------------ /** Returns the distance from start. */ float getDistanceFromStart() const - { return m_distance_from_start; } + { return m_distance_from_start; } // ------------------------------------------------------------------------ /** Sets the distance from start for this node. */ - void setDistanceFromStart(float d) {m_distance_from_start = d; } + void setDistanceFromStart(float d) { m_distance_from_start = d; } // ------------------------------------------------------------------------ /** Returns the width of the part for this quad. */ - float getPathWidth() const { return m_width; } + float getPathWidth() const { return m_width; } // ------------------------------------------------------------------------ - /** Returns the center point of the lower edge of this graph node. */ - const Vec3& getLowerCenter() const {return m_lower_center;} + /** Returns the center point of the lower edge of this drive node. */ + const Vec3& getLowerCenter() const { return m_lower_center; } // ------------------------------------------------------------------------ - /** Returns the center point of the upper edge of this graph node. */ - const Vec3& getUpperCenter() const {return m_upper_center;} + /** Returns the center point of the upper edge of this drive node. */ + const Vec3& getUpperCenter() const { return m_upper_center; } // ------------------------------------------------------------------------ /** Returns the length of the quad of this node. */ float getNodeLength() const - {return (m_lower_center-m_upper_center).length();} + { return (m_lower_center-m_upper_center).length(); } // ------------------------------------------------------------------------ bool ignoreSuccessorForAI(unsigned int i) const; // ------------------------------------------------------------------------ /** Returns which successor node to use in order to be able to reach the * given node n. - * \param n Index of the graph node to reach. + * \param n Index of the drive node to reach. */ int getSuccessorToReach(unsigned int n) { @@ -189,7 +175,7 @@ public: return m_path_to_node.size()>0 ? m_path_to_node[n] : 0; } // getSuccesorToReach // ------------------------------------------------------------------------ - /** Returns the checkline requirements of this graph node. */ + /** Returns the checkline requirements of this drive node. */ const std::vector& getChecklineRequirements() const { return m_checkline_requirements; } // ------------------------------------------------------------------------ @@ -201,17 +187,13 @@ public: } // ------------------------------------------------------------------------ /** Returns a unit vector pointing to the right side of the quad. */ - const Vec3 &getRightUnitVector() const { return m_right_unit_vector; } - // ------------------------------------------------------------------------ - /** Returns true of this node is invisible, i.e. not to be shown in - * the minimap. */ - bool isInvisible() const { return m_invisible; } + const Vec3 &getRightUnitVector() const { return m_right_unit_vector; } // ------------------------------------------------------------------------ /** True if this node should be ignored by the AI. */ - bool letAIIgnore() const { return m_ai_ignore; } + bool letAIIgnore() const { return m_ai_ignore; } // ------------------------------------------------------------------------ virtual void getDistances(const Vec3 &xyz, Vec3 *result) const = 0; -}; // GraphNode +}; // DriveNode #endif diff --git a/src/tracks/node_2d.cpp b/src/tracks/drive_node_2d.cpp similarity index 83% rename from src/tracks/node_2d.cpp rename to src/tracks/drive_node_2d.cpp index 2f6b7c2f2..058b6f159 100644 --- a/src/tracks/node_2d.cpp +++ b/src/tracks/drive_node_2d.cpp @@ -16,13 +16,15 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include "tracks/node_2d.hpp" +#include "tracks/drive_node_2d.hpp" // ---------------------------------------------------------------------------- -Node2D::Node2D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, - const Vec3 &normal, unsigned int node_index, bool invisible, - bool ai_ignore) - : GraphNode(p0, p1, p2, p3, normal, node_index, invisible, ai_ignore) +DriveNode2D::DriveNode2D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, + const Vec3 &p3, const Vec3 &normal, + unsigned int node_index, bool invisible, + bool ai_ignore) + : DriveNode(p0, p1, p2, p3, normal, node_index, invisible, + ai_ignore) { m_line = core::line2df(m_upper_center.getX(), m_upper_center.getZ(), m_lower_center.getX(), m_lower_center.getZ()); @@ -30,7 +32,7 @@ Node2D::Node2D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, // Only this 2d point is needed later m_lower_center_2d = core::vector2df(m_lower_center.getX(), m_lower_center.getZ()); -} // Node2D +} // DriveNode2D // ---------------------------------------------------------------------------- /** Returns the distance a point has from this node in forward and sidewards @@ -41,7 +43,7 @@ Node2D::Node2D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, * \param result The X coordinate contains the sidewards distance, the * Z coordinate the forward distance. */ -void Node2D::getDistances(const Vec3 &xyz, Vec3 *result) const +void DriveNode2D::getDistances(const Vec3 &xyz, Vec3 *result) const { core::vector2df xyz2d(xyz.getX(), xyz.getZ()); core::vector2df closest = m_line.getClosestPoint(xyz2d); @@ -61,7 +63,7 @@ void Node2D::getDistances(const Vec3 &xyz, Vec3 *result) const * which belongs to this graph node. The value is computed in 2d only! * \param xyz The point for which the distance to the line is computed. */ -float Node2D::getDistance2FromPoint(const Vec3 &xyz) const +float DriveNode2D::getDistance2FromPoint(const Vec3 &xyz) const { core::vector2df xyz2d(xyz.getX(), xyz.getZ()); core::vector2df closest = m_line.getClosestPoint(xyz2d); diff --git a/src/tracks/node_2d.hpp b/src/tracks/drive_node_2d.hpp similarity index 84% rename from src/tracks/node_2d.hpp rename to src/tracks/drive_node_2d.hpp index 036e7cbf1..c5a7b6f3e 100644 --- a/src/tracks/node_2d.hpp +++ b/src/tracks/drive_node_2d.hpp @@ -16,10 +16,10 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_NODE_2D_HPP -#define HEADER_NODE_2D_HPP +#ifndef HEADER_DRIVE_NODE_2D_HPP +#define HEADER_DRIVE_NODE_2D_HPP -#include "tracks/graph_node.hpp" +#include "tracks/drive_node.hpp" #include "utils/cpp2011.hpp" #include @@ -27,7 +27,7 @@ /** * \ingroup tracks */ -class Node2D : public GraphNode +class DriveNode2D : public DriveNode { private: /** The center point of the lower two points (e.g. points 0 and 1). @@ -43,9 +43,9 @@ private: core::line2df m_line; public: - Node2D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, - const Vec3 &normal, unsigned int node_index, bool invisible, - bool ai_ignore); + DriveNode2D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, + const Vec3 &normal, unsigned int node_index, bool invisible, + bool ai_ignore); // ------------------------------------------------------------------------ virtual void getDistances(const Vec3 &xyz, Vec3 *result) const OVERRIDE; // ------------------------------------------------------------------------ diff --git a/src/tracks/node_3d.cpp b/src/tracks/drive_node_3d.cpp similarity index 83% rename from src/tracks/node_3d.cpp rename to src/tracks/drive_node_3d.cpp index 7cd7b3c4e..be8f83752 100644 --- a/src/tracks/node_3d.cpp +++ b/src/tracks/drive_node_3d.cpp @@ -16,18 +16,20 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include "tracks/node_3d.hpp" +#include "tracks/drive_node_3d.hpp" // ---------------------------------------------------------------------------- -Node3D::Node3D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, - const Vec3 &normal, unsigned int node_index, bool invisible, - bool ai_ignore) - : GraphNode(p0, p1, p2, p3, normal, node_index, invisible, ai_ignore) +DriveNode3D::DriveNode3D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, + const Vec3 &p3, const Vec3 &normal, + unsigned int node_index, bool invisible, + bool ai_ignore) + : DriveNode(p0, p1, p2, p3, normal, node_index, invisible, + ai_ignore) { BoundingBox3D::init(p0, p1, p2, p3, normal); m_line = core::line3df(m_lower_center.toIrrVector(), m_upper_center.toIrrVector()); -} // Node3D +} // DriveNode3D // ---------------------------------------------------------------------------- /** Returns the distance a point has from this node in forward and sidewards @@ -38,7 +40,7 @@ Node3D::Node3D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, * \param result The X coordinate contains the sidewards distance, the * Z coordinate the forward distance. */ -void Node3D::getDistances(const Vec3 &xyz, Vec3 *result) const +void DriveNode3D::getDistances(const Vec3 &xyz, Vec3 *result) const { core::vector3df xyz_irr = xyz.toIrrVector(); core::vector3df closest = m_line.getClosestPoint(xyz.toIrrVector()); @@ -59,7 +61,7 @@ void Node3D::getDistances(const Vec3 &xyz, Vec3 *result) const * which belongs to this node. * \param xyz The point for which the distance to the line is computed. */ -float Node3D::getDistance2FromPoint(const Vec3 &xyz) const +float DriveNode3D::getDistance2FromPoint(const Vec3 &xyz) const { core::vector3df closest = m_line.getClosestPoint(xyz.toIrrVector()); return (closest-xyz.toIrrVector()).getLengthSQ(); diff --git a/src/tracks/node_3d.hpp b/src/tracks/drive_node_3d.hpp similarity index 81% rename from src/tracks/node_3d.hpp rename to src/tracks/drive_node_3d.hpp index ec66ea9aa..e3bde50c3 100644 --- a/src/tracks/node_3d.hpp +++ b/src/tracks/drive_node_3d.hpp @@ -16,18 +16,18 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_NODE_3D_HPP -#define HEADER_NODE_3D_HPP +#ifndef HEADER_DRIVE_NODE_3D_HPP +#define HEADER_DRIVE_NODE_3D_HPP #include "tracks/bounding_box_3d.hpp" -#include "tracks/graph_node.hpp" +#include "tracks/drive_node.hpp" #include "utils/cpp2011.hpp" /** * \ingroup tracks */ -class Node3D : public GraphNode, - public BoundingBox3D +class DriveNode3D : public DriveNode, + public BoundingBox3D { private: /** Line between lower and upper center, saves computation in @@ -36,9 +36,9 @@ private: core::line3df m_line; public: - Node3D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, - const Vec3 &normal, unsigned int node_index, bool invisible, - bool ai_ignore); + DriveNode3D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, + const Vec3 &normal, unsigned int node_index, bool invisible, + bool ai_ignore); // ------------------------------------------------------------------------ virtual bool pointInside(const Vec3& p, bool ignore_vertical = false) const OVERRIDE diff --git a/src/tracks/graph.cpp b/src/tracks/graph.cpp index 4acfede90..ea9a481c3 100644 --- a/src/tracks/graph.cpp +++ b/src/tracks/graph.cpp @@ -30,8 +30,8 @@ #include "graphics/rtts.hpp" #include "modes/profile_world.hpp" #include "tracks/arena_node_3d.hpp" -#include "tracks/node_2d.hpp" -#include "tracks/node_3d.hpp" +#include "tracks/drive_node_2d.hpp" +#include "tracks/drive_node_3d.hpp" #include "utils/log.hpp" const int Graph::UNKNOWN_SECTOR = -1; @@ -426,7 +426,7 @@ void Graph::createQuad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, } else { - q = new Node3D(p0, p1, p2, p3, normal, node_index, invisible, + q = new DriveNode3D(p0, p1, p2, p3, normal, node_index, invisible, ai_ignore); } } @@ -440,7 +440,7 @@ void Graph::createQuad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, } else { - q = new Node2D(p0, p1, p2, p3, normal, node_index, invisible, + q = new DriveNode2D(p0, p1, p2, p3, normal, node_index, invisible, ai_ignore); } } @@ -614,5 +614,3 @@ int Graph::findOutOfRoadSector(const Vec3& xyz, const int curr_sector, } return min_sector; } // findOutOfRoadSector - -//----------------------------------------------------------------------------- diff --git a/src/tracks/graph.hpp b/src/tracks/graph.hpp index 2925bf584..ba5e89ac7 100644 --- a/src/tracks/graph.hpp +++ b/src/tracks/graph.hpp @@ -40,8 +40,15 @@ class Quad; class RTT; /** - * \ingroup tracks - */ + * \brief This class stores a graph of quads. It uses a 'simplified singleton' + * design pattern: it has a static create function to create exactly instance, + * a destroy function, and a get function (that does not have the side effect + * of the 'normal singleton' design pattern to create an instance). Besides + * saving on the if statement in get(), this is necessary since certain race + * modes might not have a quad graph at all (e.g. arena without navmesh). So + * get() returns NULL in this case, and this is tested where necessary. + * \ingroup tracks + */ class Graph : public NoCopy { protected: @@ -55,7 +62,8 @@ protected: Vec3 m_bb_max; // ------------------------------------------------------------------------ - /** Factory method to dynamic create 2d / 3d quad. */ + /** Factory method to dynamic create 2d / 3d quad for drive and arena + * graph. */ void createQuad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, unsigned int node_index, bool invisible, bool ai_ignore, bool is_arena); @@ -90,11 +98,11 @@ public: static const int UNKNOWN_SECTOR; // ------------------------------------------------------------------------ /** Returns the one instance of this object. It is possible that there - * is no instance created (e.g. battle mode without navmesh) so we don't - * assert that an instance exist. */ + * is no instance created (e.g. arena without navmesh) so we don't assert + * that an instance exist. */ static Graph* get() { return m_graph; } // ------------------------------------------------------------------------ - /** Set the graph (either driveline or arena graph for now). */ + /** Set the graph (either drive or arena graph for now). */ static void setGraph(Graph* graph) { assert(m_graph == NULL); @@ -102,8 +110,8 @@ public: } // create // ------------------------------------------------------------------------ /** Cleans up the graph. It is possible that this function is called even - * if no instance exists (e.g. in battle mode without navmesh). So it is - * not an error if there is no instance. */ + * if no instance exists (e.g. arena without navmesh). So it is not an + * error if there is no instance. */ static void destroy() { if (m_graph) diff --git a/src/tracks/graph_structure.cpp b/src/tracks/graph_structure.cpp deleted file mode 100644 index 3dfe9aa28..000000000 --- a/src/tracks/graph_structure.cpp +++ /dev/null @@ -1,386 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2015 SuperTuxKart Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "tracks/graph_structure.hpp" - -#include -#include -#include -#include - -#include "graphics/irr_driver.hpp" -#include "graphics/glwrap.hpp" -#include "graphics/shaders.hpp" -#include "graphics/rtts.hpp" -#include "modes/world.hpp" -#include "modes/profile_world.hpp" -#include "utils/log.hpp" - -// ----------------------------------------------------------------------------- - -GraphStructure::GraphStructure() -{ - m_min_coord = 0; - m_scaling = 0; - m_node = NULL; - m_mesh = NULL; - m_mesh_buffer = NULL; - m_new_rtt = NULL; -} // GraphStructure - -// ----------------------------------------------------------------------------- - -void GraphStructure::destroyRTT() -{ - if (m_new_rtt != NULL) - { - delete m_new_rtt; - m_new_rtt = NULL; - } -} // destroyRTT - -// ----------------------------------------------------------------------------- -/** Cleans up the debug mesh */ -void GraphStructure::cleanupDebugMesh() -{ - if (m_node != NULL) - irr_driver->removeNode(m_node); - - m_node = NULL; - // No need to call irr_driber->removeMeshFromCache, since the mesh - // was manually made and so never added to the mesh cache. - m_mesh->drop(); - m_mesh = NULL; -} - -// ----------------------------------------------------------------------------- -/** Creates the debug mesh to display the graph on top of the track - * model. */ -void GraphStructure::createDebugMesh() -{ - if (getNumNodes() <= 0) return; // no debug output if not graph - - createMesh(/*show_invisible*/true, - /*enable_transparency*/true); - - video::S3DVertex *v = (video::S3DVertex*)m_mesh_buffer->getVertices(); - for (unsigned int i = 0; i < m_mesh_buffer->getVertexCount(); i++) - { - // Swap the alpha and back - v[i].Color.setAlpha((i%2) ? 64 : 255); - } - m_node = irr_driver->addMesh(m_mesh, "track-debug-mesh"); -#ifdef DEBUG - m_node->setName("track-debug-mesh"); -#endif - -} // createDebugMesh - -// ----------------------------------------------------------------------------- -/** Creates the actual mesh that is used by createDebugMesh() or makeMiniMap() */ -void GraphStructure::createMesh(bool show_invisible, - bool enable_transparency, - const video::SColor *track_color) -{ - // The debug track will not be lighted or culled. - video::SMaterial m; - m.BackfaceCulling = false; - m.Lighting = false; - if (enable_transparency) - m.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - m.setTexture(0, getUnicolorTexture(video::SColor(255, 255, 255, 255))); - m.setTexture(1, getUnicolorTexture(video::SColor(0, 0, 0, 0))); - m.setTexture(7, getUnicolorTexture(video::SColor(0, 0, 0, 0))); - m_mesh = irr_driver->createQuadMesh(&m); - m_mesh_buffer = m_mesh->getMeshBuffer(0); - assert(m_mesh_buffer->getVertexType()==video::EVT_STANDARD); - - unsigned int n = 0; - const unsigned int total_nodes = getNumNodes(); - - // Count the number of quads to display (some quads might be invisible) - for (unsigned int i = 0; i < total_nodes; i++) - { - if (show_invisible || !isNodeInvisible(i)) - n++; - } - - // Four vertices for each of the n-1 remaining quads - video::S3DVertex *new_v = new video::S3DVertex[4*n]; - // Each quad consists of 2 triangles with 3 elements, so - // we need 2*3 indices for each quad. - irr::u16 *ind = new irr::u16[6*n]; - video::SColor c(255, 255, 0, 0); - - if (track_color) - c = *track_color; - - // Now add all quads - int i = 0; - for (unsigned int count = 0; count < total_nodes; count++) - { - // Ignore invisible quads - if (!show_invisible && isNodeInvisible(count)) - continue; - - // Swap the colours from red to blue and back - if (!track_color) - { - c.setRed ((i%2) ? 255 : 0); - c.setBlue((i%2) ? 0 : 255); - } - - NodeColor nc = COLOR_RED; - const bool different_color = differentNodeColor(count, &nc); - // Transfer the 4 points of the current quad to the list of vertices - set3DVerticesOfGraph(count, new_v+4*i, (different_color ? - (nc == COLOR_RED ? video::SColor(255, 255, 0, 0) : - nc == COLOR_GREEN ? video::SColor(255, 0, 255, 0) : - nc == COLOR_BLUE ? video::SColor(255, 0, 0, 255) : - video::SColor(255, 255, 255, 0)) : c)); - - // Set up the indices for the triangles - // (note, afaik with opengl we could use quads directly, but the code - // would not be portable to directx anymore). - ind[6*i ] = 4*i+2; // First triangle: vertex 0, 1, 2 - ind[6*i+1] = 4*i+1; - ind[6*i+2] = 4*i; - ind[6*i+3] = 4*i+3; // second triangle: vertex 0, 1, 3 - ind[6*i+4] = 4*i+2; - ind[6*i+5] = 4*i; - i++; - } - - m_mesh_buffer->append(new_v, n*4, ind, n*6); - - if (0) - { - video::S3DVertex lap_v[4]; - irr::u16 lap_ind[6]; - video::SColor lap_color(128, 255, 0, 0); - set3DVerticesOfGraph(0, lap_v, lap_color); - - // Now scale the length (distance between vertix 0 and 3 - // and between 1 and 2) to be 'length': - Vec3 bb_min, bb_max; - getGraphBoundingBox(&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; - - core::vector3df dl = lap_v[3].Pos-lap_v[0].Pos; - float ll2 = dl.getLengthSQ(); - if (ll2 < 0.001) - lap_v[3].Pos = lap_v[0].Pos+core::vector3df(0, 0, 1); - else - lap_v[3].Pos = lap_v[0].Pos+dl*length/sqrt(ll2); - - core::vector3df dr = lap_v[2].Pos-lap_v[1].Pos; - float lr2 = dr.getLengthSQ(); - if (lr2 < 0.001) - lap_v[2].Pos = lap_v[1].Pos+core::vector3df(0, 0, 1); - else - lap_v[2].Pos = lap_v[1].Pos+dr*length/sqrt(lr2); - lap_ind[0] = 2; - lap_ind[1] = 1; - lap_ind[2] = 0; - lap_ind[3] = 3; - lap_ind[4] = 2; - lap_ind[5] = 0; - // Set it a bit higher to avoid issued with z fighting, - // i.e. part of the lap line might not be visible. - for (unsigned int i = 0; i < 4; i++) - lap_v[i].Pos.Y += 0.1f; -#ifndef USE_TEXTURED_LINE - m_mesh_buffer->append(lap_v, 4, lap_ind, 6); -#else - lap_v[0].TCoords = core::vector2df(0,0); - lap_v[1].TCoords = core::vector2df(3,0); - lap_v[2].TCoords = core::vector2df(3,1); - lap_v[3].TCoords = core::vector2df(0,1); - m_mesh_buffer->append(lap_v, 4, lap_ind, 6); - video::SMaterial &m = m_mesh_buffer->getMaterial(); - video::ITexture *t = irr_driver->getTexture("chess.png"); - m.setTexture(0, t); -#endif - } - - // Instead of setting the bounding boxes, we could just disable culling, - // since the debug track should always be drawn. - //m_node->setAutomaticCulling(scene::EAC_OFF); - m_mesh_buffer->recalculateBoundingBox(); - m_mesh->setBoundingBox(m_mesh_buffer->getBoundingBox()); - - m_mesh_buffer->getMaterial().setTexture(0, irr_driver->getTexture("unlit.png")); - - delete[] ind; - delete[] new_v; -} // createMesh - -// ----------------------------------------------------------------------------- -/** Takes a snapshot of the graph so they can be used as minimap. - */ -void GraphStructure::makeMiniMap(const core::dimension2du &dimension, - const std::string &name, - const video::SColor &fill_color, - video::ITexture** oldRttMinimap, - FrameBuffer** newRttMinimap) -{ - // Skip minimap when profiling - if (ProfileWorld::isNoGraphics()) return; - - const video::SColor oldClearColor = World::getWorld()->getClearColor(); - World::getWorld()->setClearbackBufferColor(video::SColor(0, 255, 255, 255)); - World::getWorld()->forceFogDisabled(true); - *oldRttMinimap = NULL; - *newRttMinimap = NULL; - - RTT* newRttProvider = NULL; - IrrDriver::RTTProvider* oldRttProvider = NULL; - if (CVS->isGLSL()) - { - m_new_rtt = newRttProvider = new RTT(dimension.Width, dimension.Height); - } - else - { - oldRttProvider = new IrrDriver::RTTProvider(dimension, name, true); - } - - irr_driver->getSceneManager()->setAmbientLight(video::SColor(255, 255, 255, 255)); - - createMesh(/*show_invisible part of the track*/ false, - /*enable_transparency*/ false, - /*track_color*/ &fill_color); - - m_node = irr_driver->addMesh(m_mesh, "mini_map"); -#ifdef DEBUG - m_node->setName("minimap-mesh"); -#endif - - m_node->setAutomaticCulling(0); - m_node->setMaterialFlag(video::EMF_LIGHTING, false); - - // Add the camera: - // --------------- - scene::ICameraSceneNode *camera = irr_driver->addCameraSceneNode(); - Vec3 bb_min, bb_max; - getGraphBoundingBox(&bb_min, &bb_max); - Vec3 center = (bb_max+bb_min)*0.5f; - - float dx = bb_max.getX()-bb_min.getX(); - float dz = bb_max.getZ()-bb_min.getZ(); - - // Set the scaling correctly. Also the center point (which is used - // as the camera position) needs to be adjusted: the track must - // be aligned to the left/top of the texture which is used (otherwise - // mapPoint2MiniMap doesn't work), so adjust the camera position - // that the track is properly aligned (view from the side): - // c camera - // / \ . - // / \ <--- camera angle - // / \ . - // { [-] } <--- track flat (viewed from the side) - // If [-] is the shorter side of the track, then the camera will - // actually render the area in { } - which is the length of the - // longer side of the track. - // To align the [-] side to the left, the camera must be moved - // the distance betwwen '{' and '[' to the right. This distance - // is exacly (longer_side - shorter_side) / 2. - // So, adjust the center point by this amount: - if (dz > dx) - { - center.setX(center.getX() + (dz-dx)*0.5f); - m_scaling = dimension.Width / dz; - } - else - { - center.setZ(center.getZ() + (dx-dz)*0.5f); - m_scaling = dimension.Width / dx; - } - - float range = (dx>dz) ? dx : dz; - - core::matrix4 projection; - projection.buildProjectionMatrixOrthoLH(range /* width */, - range /* height */, - -1, bb_max.getY()-bb_min.getY()+1); - camera->setProjectionMatrix(projection, true); - - irr_driver->suppressSkyBox(); - irr_driver->clearLights(); - - // Adjust Y position by +1 for max, -1 for min - this helps in case that - // the maximum Y coordinate is negative (otherwise the minimap is mirrored) - // and avoids problems for tracks which have a flat (max Y = min Y) minimap. - camera->setPosition(core::vector3df(center.getX(), bb_min.getY() + 1.0f, center.getZ())); - //camera->setPosition(core::vector3df(center.getX() - 5.0f, bb_min.getY() - 1 - 5.0f, center.getZ() - 15.0f)); - camera->setUpVector(core::vector3df(0, 0, 1)); - camera->setTarget(core::vector3df(center.getX(),bb_min.getY()-1,center.getZ())); - //camera->setAspectRatio(1.0f); - camera->updateAbsolutePosition(); - - video::ITexture* texture = NULL; - FrameBuffer* frame_buffer = NULL; - - if (CVS->isGLSL()) - { - frame_buffer = newRttProvider->render(camera, GUIEngine::getLatestDt()); - } - else - { - texture = oldRttProvider->renderToTexture(); - delete oldRttProvider; - } - - cleanupDebugMesh(); - irr_driver->removeCameraSceneNode(camera); - m_min_coord = bb_min; - - - if (texture == NULL && frame_buffer == NULL) - { - Log::error("Graph Structure", "[makeMiniMap] WARNING: RTT does not" - "appear to work, mini-map will not be available."); - } - - *oldRttMinimap = texture; - *newRttMinimap = frame_buffer; - World::getWorld()->setClearbackBufferColor(oldClearColor); - World::getWorld()->forceFogDisabled(false); - - irr_driver->getSceneManager()->clear(); - VAOManager::kill(); - irr_driver->clearGlowingNodes(); - irr_driver->clearLights(); - irr_driver->clearForcedBloom(); - irr_driver->clearBackgroundNodes(); -} // makeMiniMap - -// ----------------------------------------------------------------------------- -/** Returns the 2d coordinates of a point when drawn on the mini map - * texture. - * \param xyz Coordinates of the point to map. - * \param draw_at The coordinates in pixel on the mini map of the point, - * only the first two coordinates will be used. - */ -void GraphStructure::mapPoint2MiniMap(const Vec3 &xyz,Vec3 *draw_at) const -{ - draw_at->setX((xyz.getX()-m_min_coord.getX())*m_scaling); - draw_at->setY((xyz.getZ()-m_min_coord.getZ())*m_scaling); - -} // mapPoint diff --git a/src/tracks/graph_structure.hpp b/src/tracks/graph_structure.hpp deleted file mode 100644 index e6f159e4c..000000000 --- a/src/tracks/graph_structure.hpp +++ /dev/null @@ -1,104 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2015 SuperTuxKart Team -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef HEADER_GRAPH_STRUCTURE_HPP -#define HEADER_GRAPH_STRUCTURE_HPP - -#include - -#include -#include -#include "utils/vec3.hpp" -#include "utils/no_copy.hpp" - -namespace irr -{ - namespace scene { class ISceneNode; class IMesh; class IMeshBuffer; } - namespace video { class ITexture; struct S3DVertex; } -} -using namespace irr; - -class FrameBuffer; -class RTT; - -/** - * \brief Virtual base class for a graph structure. - * This is mainly used for drawing minimap in game. - * - * \ingroup tracks - */ -class GraphStructure : public NoCopy -{ -protected: - - /** Used by soccer field with navmesh to draw goal line, - * or to determine 2d/3d nodes in driveline graph. */ - enum NodeColor - { - COLOR_BLUE, - COLOR_GREEN, - COLOR_RED, - COLOR_YELLOW - }; - - void cleanupDebugMesh(); - void destroyRTT(); - -private: - RTT* m_new_rtt; - - /** The node of the graph mesh. */ - scene::ISceneNode *m_node; - - /** The mesh of the graph mesh. */ - scene::IMesh *m_mesh; - - /** The actual mesh buffer storing the graph. */ - scene::IMeshBuffer *m_mesh_buffer; - - /** The minimum coordinates of the graph. */ - Vec3 m_min_coord; - - /** Scaling for mini map. */ - float m_scaling; - - void createMesh(bool show_invisible=true, - bool enable_transparency=false, - const video::SColor *track_color=NULL); - - virtual void set3DVerticesOfGraph(int i, video::S3DVertex *v, - const video::SColor &color) const = 0; - virtual void getGraphBoundingBox(Vec3 *min, Vec3 *max) const = 0; - virtual const bool isNodeInvisible(int n) const = 0; - virtual const bool hasLapLine() const = 0; - virtual const bool differentNodeColor(int n, NodeColor* c) const = 0; - -public: - GraphStructure(); - virtual ~GraphStructure() {}; - void createDebugMesh(); - void makeMiniMap(const core::dimension2du &where, - const std::string &name, - const video::SColor &fill_color, - video::ITexture** oldRttMinimap, - FrameBuffer** newRttMinimap); - void mapPoint2MiniMap(const Vec3 &xyz, Vec3 *out) const; - virtual const unsigned int getNumNodes() const = 0; -}; // GraphStructure - -#endif diff --git a/src/tracks/quad_graph.hpp b/src/tracks/quad_graph.hpp deleted file mode 100644 index c93748b50..000000000 --- a/src/tracks/quad_graph.hpp +++ /dev/null @@ -1,182 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2009-2015 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 3 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, B - -#ifndef HEADER_QUAD_GRAPH_HPP -#define HEADER_QUAD_GRAPH_HPP - -#include -#include -#include - -#include "tracks/graph_structure.hpp" -#include "utils/aligned_array.hpp" - -#include "LinearMath/btTransform.h" - -class GraphNode; -class XMLNode; - -/** - * \brief This class stores a graph of quads. It uses a 'simplified singleton' - * design pattern: it has a static create function to create exactly instance, - * a destroy function, and a get function (that does not have the side effect - * of the 'normal singleton' design pattern to create an instance). Besides - * saving on the if statement in get(), this is necessary since certain race - * modes might not have a quad graph at all (e.g. battle mode). So get() - * returns NULL in this case, and this is tested where necessary. - * \ingroup tracks - */ -class QuadGraph : public GraphStructure -{ - -private: - static QuadGraph *m_quad_graph; - - /** The 2d bounding box, used for hashing. */ - Vec3 m_min; - Vec3 m_max; - - /** The actual graph data structure. */ - std::vector m_all_nodes; - - /** The length of the first loop. */ - float m_lap_length; - - /** 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 computeChecklineRequirements(GraphNode* node, int latest_checkline); - void computeDirectionData(); - void determineDirection(unsigned int current, unsigned int succ_index); - float normalizeAngle(float f); - - void addSuccessor(unsigned int from, unsigned int to); - void load(const std::string &quad_file_name, const std::string &filename); - void getPoint(const XMLNode *xml, const std::string &attribute_name, - Vec3 *result) const; - void computeDistanceFromStart(unsigned int start_node, float distance); - unsigned int getStartNode() const; - QuadGraph(const std::string &quad_file_name, - const std::string &graph_file_name, - const bool reverse); - ~QuadGraph(); - - // ------------------------------------------------------------------------ - virtual void set3DVerticesOfGraph(int i, video::S3DVertex *v, - const video::SColor &color) const; - // ------------------------------------------------------------------------ - virtual void getGraphBoundingBox(Vec3 *min, Vec3 *max) const - { *min = m_min; *max = m_max; } - // ------------------------------------------------------------------------ - virtual const bool isNodeInvisible(int n) const; - // ------------------------------------------------------------------------ - virtual const bool hasLapLine() const - { return true; } - // ------------------------------------------------------------------------ - virtual const bool differentNodeColor(int n, NodeColor* c) const; - -public: - static const int UNKNOWN_SECTOR; - - void getSuccessors(int node_number, - std::vector& succ, - bool for_ai=false) const; - void spatialToTrack(Vec3 *dst, const Vec3& xyz, - const int sector) const; - void findRoadSector(const Vec3& XYZ, int *sector, - std::vector *all_sectors=NULL) const; - int findOutOfRoadSector(const Vec3& xyz, - const int curr_sector=UNKNOWN_SECTOR, - std::vector *all_sectors=NULL - ) const; - void setDefaultStartPositions(AlignedArray - *start_transforms, - unsigned int karts_per_row, - float forwards_distance=1.5f, - float sidewards_distance=1.5f, - float upwards_distance=0.0f) const; - void updateDistancesForAllSuccessors(unsigned int indx, - float delta, - unsigned int count); - 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 - * also don't create one if it doesn't exists. */ - static QuadGraph *get() { return m_quad_graph; } - // ------------------------------------------------------------------------ - /** Creates a QuadGraph instance. */ - static void create(const std::string &quad_file_name, - const std::string &graph_file_name, - const bool reverse) - { - assert(m_quad_graph==NULL); - // 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 - * even if no instance exists (e.g. in battle mode). So it is not an - * error if there is no instance. */ - static void destroy() - { - if(m_quad_graph) - { - delete m_quad_graph; - m_quad_graph = NULL; - } - } // destroy - // ------------------------------------------------------------------------ - /** Returns the number of nodes in the graph. */ - virtual const unsigned int getNumNodes() const - { return m_all_nodes.size(); } - // ------------------------------------------------------------------------ - /** Return the distance to the j-th successor of node n. */ - float getDistanceToNext(int n, int j) const; - // ------------------------------------------------------------------------ - /** Returns the angle of the line between node n and its j-th. - * successor. */ - float getAngleToNext(int n, int j) const; - // ------------------------------------------------------------------------ - /** Returns the number of successors of a node n. */ - int getNumberOfSuccessors(int n) const; - // ------------------------------------------------------------------------ - /** Returns the quad that belongs to a graph node. */ - GraphNode* getNode(unsigned int j) { return m_all_nodes[j]; } - // ------------------------------------------------------------------------ - /** Returns the quad that belongs to a graph node (const version). */ - const GraphNode* getNode(unsigned int j) const { return m_all_nodes[j]; } - // ------------------------------------------------------------------------ - /** Returns the distance from the start to the beginning of a quad. */ - float getDistanceFromStart(int j) const; - // ------------------------------------------------------------------------ - /** Returns the length of the main driveline. */ - float getLapLength() const { return m_lap_length; } - // ------------------------------------------------------------------------ - bool isReverse() const { return m_reverse; } - -}; // QuadGraph - -#endif diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index f4531f0f6..107ef4f7d 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -56,9 +56,9 @@ #include "tracks/arena_graph.hpp" #include "tracks/bezier_curve.hpp" #include "tracks/check_manager.hpp" +#include "tracks/drive_graph.hpp" +#include "tracks/drive_node.hpp" #include "tracks/model_definition_loader.hpp" -#include "tracks/node_3d.hpp" -#include "tracks/quad_graph.hpp" #include "tracks/track_manager.hpp" #include "tracks/track_object_manager.hpp" #include "utils/constants.hpp" @@ -277,7 +277,6 @@ void Track::reset() */ void Track::cleanup() { - QuadGraph::destroy(); Graph::destroy(); ItemManager::destroy(); VAOManager::kill(); @@ -374,7 +373,7 @@ void Track::cleanup() } if (m_new_rtt_mini_map) { - m_new_rtt_mini_map = NULL; // already deleted by QuadGraph::~QuadGraph + m_new_rtt_mini_map = NULL; // already deleted by Graph::~Graph } for(unsigned int i=0; isetupPaths(); + // setGraph is done in DriveGraph constructor + assert(DriveGraph::get()); + DriveGraph::get()->setupPaths(); #ifdef DEBUG - for(unsigned int i=0; igetNumNodes(); i++) + for(unsigned int i=0; igetNumNodes(); i++) { - assert(QuadGraph::get()->getNode(i)->getPredecessor(0)!=-1); + assert(DriveGraph::get()->getNode(i)->getPredecessor(0)!=-1); } #endif - if(QuadGraph::get()->getNumNodes()==0) + if(DriveGraph::get()->getNumNodes()==0) { Log::warn("track", "No graph nodes defined for track '%s'\n", m_filename.c_str()); @@ -746,16 +746,13 @@ void Track::loadQuadGraph(unsigned int mode_id, const bool reverse) { loadMinimap(); } -} // loadQuadGraph +} // loadDriveGraph // ----------------------------------------------------------------------------- void Track::mapPoint2MiniMap(const Vec3 &xyz, Vec3 *draw_at) const { - if ((m_is_arena || m_is_soccer) && m_has_navmesh) - Graph::get()->mapPoint2MiniMap(xyz, draw_at); - else - QuadGraph::get()->mapPoint2MiniMap(xyz, draw_at); + Graph::get()->mapPoint2MiniMap(xyz, draw_at); draw_at->setX(draw_at->getX() * m_minimap_x_scale); draw_at->setY(draw_at->getY() * m_minimap_y_scale); } @@ -1058,16 +1055,8 @@ void Track::loadMinimap() core::dimension2du size = m_mini_map_size .getOptimalSize(!nonpower,!nonsquare); - if ((m_is_arena || m_is_soccer) && m_has_navmesh) - { - Graph::get()->makeMiniMap(size, "minimap::" + m_ident, video::SColor(127, 255, 255, 255), - &m_old_rtt_mini_map, &m_new_rtt_mini_map); - } - else - { - QuadGraph::get()->makeMiniMap(size, "minimap::" + m_ident, video::SColor(127, 255, 255, 255), - &m_old_rtt_mini_map, &m_new_rtt_mini_map); - } + Graph::get()->makeMiniMap(size, "minimap::" + m_ident, video::SColor(127, 255, 255, 255), + &m_old_rtt_mini_map, &m_new_rtt_mini_map); if (m_old_rtt_mini_map) { @@ -1686,7 +1675,7 @@ void Track::loadTrackModel(bool reverse_track, 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 && !m_is_soccer && !m_is_cutscene) loadQuadGraph(mode_id, reverse_track); + if (!m_is_arena && !m_is_soccer && !m_is_cutscene) loadDriveGraph(mode_id, reverse_track); else if ((m_is_arena || m_is_soccer) && !m_is_cutscene && m_has_navmesh) loadArenaGraph(*root); @@ -1718,7 +1707,7 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id) } else m_start_transforms.resize(race_manager->getNumberOfKarts()); - QuadGraph::get()->setDefaultStartPositions(&m_start_transforms, + DriveGraph::get()->setDefaultStartPositions(&m_start_transforms, karts_per_row, forwards_distance, sidewards_distance, @@ -1929,7 +1918,7 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id) World *world = World::getWorld(); if (world->useChecklineRequirements()) { - QuadGraph::get()->computeChecklineRequirements(); + DriveGraph::get()->computeChecklineRequirements(); } EasterEggHunt *easter_world = dynamic_cast(world); @@ -2282,30 +2271,26 @@ void Track::itemCommand(const XMLNode *node) // Test if the item lies on a 3d node, if so adjust the normal // Also do a raycast if drop item is given Vec3 normal(0, 1, 0); + Vec3 quad_normal = normal; Vec3 hit_point = loc; - Node3D* node_3d = NULL; - if (QuadGraph::get()) + if (Graph::get()) { - int road_sector = QuadGraph::UNKNOWN_SECTOR; - QuadGraph::get()->findRoadSector(xyz, &road_sector); + int road_sector = Graph::UNKNOWN_SECTOR; + Graph::get()->findRoadSector(xyz, &road_sector); // Only do custom direction of raycast if item is on quad graph - if (road_sector != QuadGraph::UNKNOWN_SECTOR) + if (road_sector != Graph::UNKNOWN_SECTOR) { - node_3d = - dynamic_cast(QuadGraph::get()->getNode(road_sector)); + quad_normal = Graph::get()->getQuad(road_sector)->getNormal(); } } - Vec3 quad_normal = node_3d ? node_3d->getNormal() : Vec3(0, 1, 0); - if (node_3d || drop) + if (drop) { const Material *m; // If raycast is used, increase the start position slightly // in case that the point is too close to the actual surface // (e.g. floating point errors can cause a problem here). - // Only do so for 2d node - if (node_3d == NULL) - loc += Vec3(0.0f, 0.1f, 0.0f); + loc += quad_normal * 0.1; #ifndef DEBUG m_track_mesh->castRay(loc, loc + (-10000 * quad_normal), &hit_point, @@ -2323,8 +2308,7 @@ void Track::itemCommand(const XMLNode *node) #endif } - ItemManager::get()->newItem(type, drop ? hit_point : loc, - node_3d ? normal : Vec3(0, 1, 0)); + ItemManager::get()->newItem(type, drop ? hit_point : loc, normal); } // itemCommand // ---------------------------------------------------------------------------- @@ -2434,11 +2418,11 @@ bool Track::findGround(AbstractKart *kart) //----------------------------------------------------------------------------- float Track::getTrackLength() const { - return QuadGraph::get()->getLapLength(); + return DriveGraph::get()->getLapLength(); } // getTrackLength //----------------------------------------------------------------------------- float Track::getAngle(int n) const { - return QuadGraph::get()->getAngleToNext(n, 0); + return DriveGraph::get()->getAngleToNext(n, 0); } // getAngle diff --git a/src/tracks/track.hpp b/src/tracks/track.hpp index 4c7de76cf..03e4972f4 100644 --- a/src/tracks/track.hpp +++ b/src/tracks/track.hpp @@ -373,7 +373,7 @@ private: int m_actual_number_of_laps; void loadTrackInfo(); - void loadQuadGraph(unsigned int mode_id, const bool reverse); + void loadDriveGraph(unsigned int mode_id, const bool reverse); void loadArenaGraph(const XMLNode &node); btQuaternion getArenaStartRotation(const Vec3& xyz, float heading); void convertTrackToBullet(scene::ISceneNode *node); diff --git a/src/tracks/track_sector.cpp b/src/tracks/track_sector.cpp index 8f1e8a28f..c6fac42c8 100644 --- a/src/tracks/track_sector.cpp +++ b/src/tracks/track_sector.cpp @@ -16,15 +16,16 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#include "tracks/track_sector.hpp" + #include "modes/linear_world.hpp" #include "modes/world.hpp" #include "tracks/check_manager.hpp" #include "tracks/check_structure.hpp" -#include "tracks/track_sector.hpp" #include "tracks/arena_graph.hpp" #include "tracks/arena_node.hpp" -#include "tracks/quad_graph.hpp" -#include "tracks/graph_node.hpp" +#include "tracks/drive_graph.hpp" +#include "tracks/drive_node.hpp" // ---------------------------------------------------------------------------- /** Initialises the object, and sets the current graph node to be undefined. @@ -37,8 +38,8 @@ TrackSector::TrackSector() // ---------------------------------------------------------------------------- void TrackSector::reset() { - m_current_graph_node = QuadGraph::UNKNOWN_SECTOR; - m_last_valid_graph_node = QuadGraph::UNKNOWN_SECTOR; + m_current_graph_node = Graph::UNKNOWN_SECTOR; + m_last_valid_graph_node = Graph::UNKNOWN_SECTOR; m_on_road = false; m_last_triggered_checkline = -1; } // reset @@ -79,8 +80,8 @@ void TrackSector::update(const Vec3 &xyz, bool ignore_vertical) if (ag) return; // keep the current quad as the latest valid one IF the player has one // of the required checklines - const GraphNode* gn = QuadGraph::get()->getNode(m_current_graph_node); - const std::vector& checkline_requirements = gn->getChecklineRequirements(); + const DriveNode* dn = DriveGraph::get()->getNode(m_current_graph_node); + const std::vector& checkline_requirements = dn->getChecklineRequirements(); if (checkline_requirements.size() == 0) { @@ -107,8 +108,8 @@ void TrackSector::update(const Vec3 &xyz, bool ignore_vertical) // Now determine the 'track' coords, i.e. ow far from the start of the // track, and how far to the left or right of the center driveline. - QuadGraph::get()->spatialToTrack(&m_current_track_coords, xyz, - m_current_graph_node); + DriveGraph::get()->spatialToTrack(&m_current_track_coords, xyz, + m_current_graph_node); } // update // ---------------------------------------------------------------------------- @@ -116,7 +117,7 @@ void TrackSector::update(const Vec3 &xyz, bool ignore_vertical) */ void TrackSector::rescue() { - if (m_last_valid_graph_node != QuadGraph::UNKNOWN_SECTOR) + if (m_last_valid_graph_node != Graph::UNKNOWN_SECTOR) m_current_graph_node = m_last_valid_graph_node; // Using the predecessor has the additional advantage (besides punishing @@ -124,10 +125,10 @@ void TrackSector::rescue() // rescue loop since the kart moves back on each attempt. At this stage // STK does not keep track of where the kart is coming from, so always // use the first predecessor, which is the one on the main driveline. - m_current_graph_node = QuadGraph::get()->getNode(m_current_graph_node) - ->getPredecessor(0); - m_last_valid_graph_node = QuadGraph::get()->getNode(m_current_graph_node) - ->getPredecessor(0); + m_current_graph_node = DriveGraph::get()->getNode(m_current_graph_node) + ->getPredecessor(0); + m_last_valid_graph_node = DriveGraph::get()->getNode(m_current_graph_node) + ->getPredecessor(0); } // rescue // ---------------------------------------------------------------------------- @@ -137,7 +138,7 @@ void TrackSector::rescue() */ float TrackSector::getRelativeDistanceToCenter() const { - float w = QuadGraph::get()->getNode(m_current_graph_node)->getPathWidth(); + float w = DriveGraph::get()->getNode(m_current_graph_node)->getPathWidth(); // w * 0.5 is the distance from center of the quad to the left or right // This way we get a value between -1 and 1. float ratio = getDistanceToCenter()/(w*0.5f); From b137dbb71c80fc253ec1bc5bca27d385a408b52c Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 18 Sep 2016 10:53:58 +0800 Subject: [PATCH 205/350] Set gravity inside btKart, allowing starting upside down in arena --- src/karts/kart.cpp | 20 -------------------- src/physics/btKart.cpp | 17 ++++++++++++++--- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index ae049374e..ab4cf5788 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1370,13 +1370,6 @@ void Kart::update(float dt) const Material* material=m_terrain_info->getMaterial(); if (!material) // kart falling off the track { - if (!m_flying) - { - float g = World::getWorld()->getTrack()->getGravity(); - Vec3 gravity(0, -g, 0); - btRigidBody *body = getVehicle()->getRigidBody(); - body->setGravity(gravity); - } // let kart fall a bit before rescuing const Vec3 *min, *max; World::getWorld()->getTrack()->getAABB(&min, &max); @@ -1387,19 +1380,6 @@ void Kart::update(float dt) } else { - if (!m_flying) - { - float g = World::getWorld()->getTrack()->getGravity(); - Vec3 gravity(0.0f, -g, 0.0f); - btRigidBody *body = getVehicle()->getRigidBody(); - // If the material should overwrite the gravity, - if (material->hasGravity()) - { - Vec3 normal = m_terrain_info->getNormal(); - gravity = normal * -g; - } - body->setGravity(gravity); - } // if !flying handleMaterialSFX(material); if (material->isDriveReset() && isOnGround()) new RescueAnimation(this); diff --git a/src/physics/btKart.cpp b/src/physics/btKart.cpp index b2c0d50f9..5779b6e9c 100644 --- a/src/physics/btKart.cpp +++ b/src/physics/btKart.cpp @@ -25,6 +25,7 @@ #include "graphics/material.hpp" #include "karts/kart.hpp" #include "modes/world.hpp" +#include "physics/physics.hpp" #include "physics/triangle_mesh.hpp" #include "tracks/terrain_info.hpp" #include "tracks/track.hpp" @@ -375,6 +376,18 @@ void btKart::updateAllWheelPositions() // ---------------------------------------------------------------------------- void btKart::updateVehicle( btScalar step ) { + bool new_g = m_kart->getMaterial() && m_kart->getMaterial()->hasGravity(); + if (new_g) + { + getRigidBody()->setGravity(m_kart->getNormal() * World::getWorld() + ->getPhysics()->getPhysicsWorld()->getGravity().y()); + } + else + { + getRigidBody()->setGravity(World::getWorld() + ->getPhysics()->getPhysicsWorld()->getGravity()); + } + updateAllWheelPositions(); const btTransform& chassisTrans = getChassisWorldTransform(); @@ -446,9 +459,7 @@ void btKart::updateVehicle( btScalar step ) if(m_num_wheels_on_ground==0) { btVector3 kart_up = getChassisWorldTransform().getBasis().getColumn(1); - btVector3 terrain_up = - m_kart->getMaterial() && m_kart->getMaterial()->hasGravity() ? - m_kart->getNormal() : Vec3(0, 1, 0); + btVector3 terrain_up = new_g ? m_kart->getNormal() : Vec3(0, 1, 0); // Length of axis depends on the angle - i.e. the further awat // the kart is from being upright, the larger the applied impulse // will be, resulting in fast changes when the kart is on its From ffd497276b1e5e0e3340dd7eee9245d16f8a1302 Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 18 Sep 2016 12:38:36 +0800 Subject: [PATCH 206/350] Fix kart flying --- src/physics/btKart.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/physics/btKart.cpp b/src/physics/btKart.cpp index 5779b6e9c..841915993 100644 --- a/src/physics/btKart.cpp +++ b/src/physics/btKart.cpp @@ -377,12 +377,12 @@ void btKart::updateAllWheelPositions() void btKart::updateVehicle( btScalar step ) { bool new_g = m_kart->getMaterial() && m_kart->getMaterial()->hasGravity(); - if (new_g) + if (new_g && !m_kart->isFlying()) { getRigidBody()->setGravity(m_kart->getNormal() * World::getWorld() ->getPhysics()->getPhysicsWorld()->getGravity().y()); } - else + else if (!m_kart->isFlying()) { getRigidBody()->setGravity(World::getWorld() ->getPhysics()->getPhysicsWorld()->getGravity()); From 3187c7e0f9f62e91451ecc5f2d185b079283fd0d Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 18 Sep 2016 13:54:00 +0800 Subject: [PATCH 207/350] Allow referee showing on upside down starting position --- src/states_screens/race_gui_base.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/states_screens/race_gui_base.cpp b/src/states_screens/race_gui_base.cpp index 03606a355..492fe0587 100644 --- a/src/states_screens/race_gui_base.cpp +++ b/src/states_screens/race_gui_base.cpp @@ -137,11 +137,14 @@ void RaceGUIBase::reset() { const AbstractKart *kart = World::getWorld()->getKart(i); m_referee_pos[i] = kart->getTrans()(Referee::getStartOffset()); - m_referee_rotation[i] = Referee::getStartRotation() - + Vec3(0, kart->getHeading()*RAD_TO_DEGREE, 0); + Vec3 hpr; + btQuaternion q = btQuaternion(kart->getTrans().getBasis().getColumn(1), + Referee::getStartRotation().getY() * DEGREE_TO_RAD) * + kart->getTrans().getRotation(); + hpr.setHPR(q); + m_referee_rotation[i] = hpr.toIrrHPR(); } - m_referee_height = 10.0f; m_referee->attachToSceneNode(); m_plunger_move_time = 0; @@ -419,8 +422,8 @@ void RaceGUIBase::preRenderCallback(const Camera *camera) if(m_referee && camera->getKart()) { unsigned int world_id = camera->getKart()->getWorldKartId(); - Vec3 xyz = m_referee_pos[world_id]; - xyz.setY(xyz.getY()+m_referee_height); + Vec3 xyz = m_referee_pos[world_id] + + camera->getKart()->getNormal() * m_referee_height; m_referee->setPosition(xyz); m_referee->setRotation(m_referee_rotation[world_id]); } From 8a0a9b26179dc7587abee25a2191c7834fdce6a9 Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 19 Sep 2016 08:49:59 +0800 Subject: [PATCH 208/350] Allow showing 3D quad in track debug better --- src/tracks/drive_node_3d.hpp | 2 ++ src/tracks/quad.cpp | 14 +++----------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/tracks/drive_node_3d.hpp b/src/tracks/drive_node_3d.hpp index e3bde50c3..5f763c3a6 100644 --- a/src/tracks/drive_node_3d.hpp +++ b/src/tracks/drive_node_3d.hpp @@ -49,6 +49,8 @@ public: virtual void getDistances(const Vec3 &xyz, Vec3 *result) const OVERRIDE; // ------------------------------------------------------------------------ virtual float getDistance2FromPoint(const Vec3 &xyz) const OVERRIDE; + // ------------------------------------------------------------------------ + virtual bool is3DQuad() const OVERRIDE { return true; } }; #endif diff --git a/src/tracks/quad.cpp b/src/tracks/quad.cpp index c63fa2dc3..2b3bf3922 100644 --- a/src/tracks/quad.cpp +++ b/src/tracks/quad.cpp @@ -46,25 +46,17 @@ void Quad::getVertices(video::S3DVertex *v, const video::SColor &color) const { // Eps is used to raise the track debug quads a little bit higher than // the ground, so that they are actually visible. - core::vector3df eps(0, 0.1f, 0); + core::vector3df normal = getNormal().toIrrVector(); + core::vector3df eps = normal * 0.1f; v[0].Pos = m_p[0].toIrrVector()+eps; v[1].Pos = m_p[1].toIrrVector()+eps; v[2].Pos = m_p[2].toIrrVector()+eps; v[3].Pos = m_p[3].toIrrVector()+eps; - core::triangle3df tri(m_p[0].toIrrVector(), m_p[1].toIrrVector(), - m_p[2].toIrrVector()); - core::vector3df normal = tri.getNormal(); - normal.normalize(); v[0].Normal = normal; v[1].Normal = normal; v[2].Normal = normal; - - core::triangle3df tri1(m_p[0].toIrrVector(), m_p[2].toIrrVector(), - m_p[3].toIrrVector()); - core::vector3df normal1 = tri1.getNormal(); - normal1.normalize(); - v[3].Normal = normal1; + v[3].Normal = normal; v[0].Color = color; v[1].Color = color; From 36eca8bc55553a09414788ed16daa2284b272779 Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 19 Sep 2016 11:18:16 +0800 Subject: [PATCH 209/350] Avoid setting gravity in btKart --- src/karts/kart.cpp | 20 ++++++++++++++++++++ src/modes/world.cpp | 8 ++++++++ src/physics/btKart.cpp | 17 +++-------------- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index ab4cf5788..ae049374e 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1370,6 +1370,13 @@ void Kart::update(float dt) const Material* material=m_terrain_info->getMaterial(); if (!material) // kart falling off the track { + if (!m_flying) + { + float g = World::getWorld()->getTrack()->getGravity(); + Vec3 gravity(0, -g, 0); + btRigidBody *body = getVehicle()->getRigidBody(); + body->setGravity(gravity); + } // let kart fall a bit before rescuing const Vec3 *min, *max; World::getWorld()->getTrack()->getAABB(&min, &max); @@ -1380,6 +1387,19 @@ void Kart::update(float dt) } else { + if (!m_flying) + { + float g = World::getWorld()->getTrack()->getGravity(); + Vec3 gravity(0.0f, -g, 0.0f); + btRigidBody *body = getVehicle()->getRigidBody(); + // If the material should overwrite the gravity, + if (material->hasGravity()) + { + Vec3 normal = m_terrain_info->getNormal(); + gravity = normal * -g; + } + body->setGravity(gravity); + } // if !flying handleMaterialSFX(material); if (material->isDriveReset() && isOnGround()) new RescueAnimation(this); diff --git a/src/modes/world.cpp b/src/modes/world.cpp index a69ff36de..c5d8a1242 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -27,6 +27,7 @@ #include "config/user_config.hpp" #include "graphics/camera.hpp" #include "graphics/irr_driver.hpp" +#include "graphics/material.hpp" #include "graphics/render_info.hpp" #include "io/file_manager.hpp" #include "input/device_manager.hpp" @@ -695,6 +696,13 @@ void World::resetAllKarts() // Do a longer initial simulation, which should be long enough for all // karts to be firmly on ground. + float g = World::getWorld()->getTrack()->getGravity(); + for (KartList::iterator i = m_karts.begin(); i != m_karts.end(); i++) + { + if ((*i)->isGhostKart()) continue; + (*i)->getBody()->setGravity((*i)->getMaterial()->hasGravity() ? + (*i)->getNormal() * -g : Vec3(0, -g, 0)); + } for(int i=0; i<60; i++) m_physics->update(1.f/60.f); for ( KartList::iterator i=m_karts.begin(); i!=m_karts.end(); i++) diff --git a/src/physics/btKart.cpp b/src/physics/btKart.cpp index 841915993..b2c0d50f9 100644 --- a/src/physics/btKart.cpp +++ b/src/physics/btKart.cpp @@ -25,7 +25,6 @@ #include "graphics/material.hpp" #include "karts/kart.hpp" #include "modes/world.hpp" -#include "physics/physics.hpp" #include "physics/triangle_mesh.hpp" #include "tracks/terrain_info.hpp" #include "tracks/track.hpp" @@ -376,18 +375,6 @@ void btKart::updateAllWheelPositions() // ---------------------------------------------------------------------------- void btKart::updateVehicle( btScalar step ) { - bool new_g = m_kart->getMaterial() && m_kart->getMaterial()->hasGravity(); - if (new_g && !m_kart->isFlying()) - { - getRigidBody()->setGravity(m_kart->getNormal() * World::getWorld() - ->getPhysics()->getPhysicsWorld()->getGravity().y()); - } - else if (!m_kart->isFlying()) - { - getRigidBody()->setGravity(World::getWorld() - ->getPhysics()->getPhysicsWorld()->getGravity()); - } - updateAllWheelPositions(); const btTransform& chassisTrans = getChassisWorldTransform(); @@ -459,7 +446,9 @@ void btKart::updateVehicle( btScalar step ) if(m_num_wheels_on_ground==0) { btVector3 kart_up = getChassisWorldTransform().getBasis().getColumn(1); - btVector3 terrain_up = new_g ? m_kart->getNormal() : Vec3(0, 1, 0); + btVector3 terrain_up = + m_kart->getMaterial() && m_kart->getMaterial()->hasGravity() ? + m_kart->getNormal() : Vec3(0, 1, 0); // Length of axis depends on the angle - i.e. the further awat // the kart is from being upright, the larger the applied impulse // will be, resulting in fast changes when the kart is on its From 1d75987fe69b1137f628cedf99000f33293cdef8 Mon Sep 17 00:00:00 2001 From: Suici Doga Date: Mon, 19 Sep 2016 13:54:42 +0530 Subject: [PATCH 210/350] Fix typo in main_loop.cpp --- src/main_loop.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main_loop.cpp b/src/main_loop.cpp index 7f9a2ca9e..e536cb3d7 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -178,7 +178,7 @@ void MainLoop::run() // Update sfx and music after graphics, so that graphics code // can use as many threads as possible without interfering - // with audia + // with audio PROFILER_PUSH_CPU_MARKER("Music/input/GUI", 0x7F, 0x00, 0x00); SFXManager::get()->update(); PROFILER_POP_CPU_MARKER(); From 54bf37c09bccf0d72d386ab1b142f3c2f04919a6 Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 20 Sep 2016 10:51:51 +0800 Subject: [PATCH 211/350] Make camera works better in upside down area --- src/graphics/camera_normal.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/graphics/camera_normal.cpp b/src/graphics/camera_normal.cpp index ea0bf098a..48a086d50 100644 --- a/src/graphics/camera_normal.cpp +++ b/src/graphics/camera_normal.cpp @@ -101,8 +101,7 @@ void CameraNormal::smoothMoveCamera(float dt) Vec3 m_kart_camera_position_with_offset = m_kart->getTrans()(camera_offset); // next target - core::vector3df current_target = m_kart->getXYZ().toIrrVector(); - current_target.Y += 0.5f; + Vec3 current_target = m_kart->getTrans()(Vec3(0, 0.5f, 0)); // new required position of camera core::vector3df wanted_position = m_kart_camera_position_with_offset.toIrrVector(); @@ -128,7 +127,7 @@ void CameraNormal::smoothMoveCamera(float dt) if(getMode()!=CM_FALLING) m_camera->setPosition(current_position); - m_camera->setTarget(current_target);//set new target + m_camera->setTarget(current_target.toIrrVector());//set new target assert(!std::isnan(m_camera->getPosition().X)); assert(!std::isnan(m_camera->getPosition().Y)); @@ -252,8 +251,7 @@ void CameraNormal::positionCamera(float dt, float above_kart, float cam_angle, float side_way, float distance, float smoothing) { Vec3 wanted_position; - Vec3 wanted_target = m_kart->getXYZ(); - wanted_target.setY(wanted_target.getY() + above_kart); + Vec3 wanted_target = m_kart->getTrans()(Vec3(0, above_kart, 0)); float tan_up = tan(cam_angle); Vec3 relative_position(side_way, From 3ccbeff1802593bb0380052ff734dddcb9cfdf85 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 21 Sep 2016 09:07:06 +1000 Subject: [PATCH 212/350] Removed exec permission. --- src/karts/abstract_characteristic.cpp | 0 src/karts/abstract_characteristic.hpp | 0 src/network/network.cpp | 0 3 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 src/karts/abstract_characteristic.cpp mode change 100755 => 100644 src/karts/abstract_characteristic.hpp mode change 100755 => 100644 src/network/network.cpp diff --git a/src/karts/abstract_characteristic.cpp b/src/karts/abstract_characteristic.cpp old mode 100755 new mode 100644 diff --git a/src/karts/abstract_characteristic.hpp b/src/karts/abstract_characteristic.hpp old mode 100755 new mode 100644 diff --git a/src/network/network.cpp b/src/network/network.cpp old mode 100755 new mode 100644 From cf3f75c6e3707a101c23e4fe24839f5171ce504b Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 21 Sep 2016 09:10:07 +1000 Subject: [PATCH 213/350] Moved SVN-CONFIG file to assets (subversion) repo. --- SVN-CONFIG | 34 ---------------------------------- 1 file changed, 34 deletions(-) delete mode 100644 SVN-CONFIG diff --git a/SVN-CONFIG b/SVN-CONFIG deleted file mode 100644 index 6c37e52d1..000000000 --- a/SVN-CONFIG +++ /dev/null @@ -1,34 +0,0 @@ -# Please, make sure your SVN client uses something that matches the -# following config. Specially the autoprops part, that way new files -# will be added with the right settings. Default config for command line -# svn is in ~/.subversion/config. If you use SVN for multiple projects -# simultaneously, remember you can use different configs, at least in -# cmd line (via --config-dir and for example ~/.subversion/supertuxkart/ -# with a new config file inside). - -[miscellany] -global-ignores = *.o *.lo *.la #*# .*.rej *.rej .*~ *~ .#* ._* .DS_Store *.blend1 *.blend2 -enable-auto-props = yes - -[auto-props] -*.c = svn:eol-style=native;svn:keywords=Author Date Id Revision -*.cpp = svn:eol-style=native;svn:keywords=Author Date Id Revision -*.h = svn:eol-style=native;svn:keywords=Author Date Id Revision -*.hpp = svn:eol-style=native;svn:keywords=Author Date Id Revision -*.dsp = svn:eol-style=CRLF -*.dsw = svn:eol-style=CRLF -*.sh = svn:eol-style=native;svn:executable;svn:keywords=Author Date Id Revision -*.txt = svn:eol-style=native -*.png = svn:mime-type=image/png -*.jpg = svn:mime-type=image/jpeg -Makefile = svn:eol-style=native;svn:keywords=Author Date Id Revision -*.jpeg = svn:mime-type=image/jpeg -*.gif = svn:mime-type=image/gif -*.svg = svn:mime-type=image/svg+xml -*.htm = svn:mime-type=text/html -*.html = svn:mime-type=text/html -*.css = svn:mime-type=text/css -*.pdf = svn:mime-type=application/pdf -SConstruct = svn:eol-style=native;svn:keywords=Author Date Id Revision -*.xml = svn:eol-style=LF;svn:mime-type=text/xml -*.py = svn:eol-style=native;svn:keywords=Author Date Id Revision From f09ed569ccafc008cedca3b98f67d48d8c02065c Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 21 Sep 2016 17:27:08 +1000 Subject: [PATCH 214/350] Bugfix: kart rewinder stored incorrect bullet zipper speed. --- src/physics/btKart.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/physics/btKart.cpp b/src/physics/btKart.cpp index 3fc9e0e06..e34549d1a 100644 --- a/src/physics/btKart.cpp +++ b/src/physics/btKart.cpp @@ -847,7 +847,7 @@ void btKart::updateFriction(btScalar timeStep) // bullet then tries to offset by applying a backward // impulse - which is a bit too big, causing a impulse // backwards, ... till the kart is shaking backwards and - // forwards. By onlyu applying half of the impulse in cae + // forwards. By only applying half of the impulse in case // of low friction this goes away. if(wheelInfo.m_brake && fabsf(rollingFriction)<10) rollingFriction*=0.5f; @@ -883,7 +883,8 @@ void btKart::updateFriction(btScalar timeStep) m_zipper_active = false; - m_zipper_speed = 0; + // Note: don't reset zipper speed, or the kart rewinder will + // get incorrect zipper information. // The kart just stopped skidding. Adjust the velocity so that // it points in the right direction. @@ -1061,7 +1062,7 @@ void btKart::setSliding(bool active) */ void btKart::instantSpeedIncreaseTo(float speed) { - m_zipper_active = true; + m_zipper_active = speed > 0; m_zipper_speed = speed; } // activateZipper From 15660f2d7abe984657ba2661542a3b1dfeb87956 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 21 Sep 2016 17:28:26 +1000 Subject: [PATCH 215/350] Bugfixes: update MaxSpeed(dt=0) (which updates additional engine power and slowdowns correctly). Call proceedToTransform after updating the velocities (which are read). --- src/karts/kart_rewinder.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/karts/kart_rewinder.cpp b/src/karts/kart_rewinder.cpp index 977d59c5c..8bd419959 100644 --- a/src/karts/kart_rewinder.cpp +++ b/src/karts/kart_rewinder.cpp @@ -111,9 +111,11 @@ void KartRewinder::rewindToState(BareNetworkString *buffer) t.setOrigin(buffer->getVec3()); t.setRotation(buffer->getQuat()); btRigidBody *body = getBody(); - body->proceedToTransform(t); body->setLinearVelocity(buffer->getVec3()); body->setAngularVelocity(buffer->getVec3()); + // This function also reads the velocity, so it must be called + // after the velocities are set + body->proceedToTransform(t); m_has_started = buffer->getUInt8()!=0; // necessary for startup speed boost m_vehicle->instantSpeedIncreaseTo(buffer->getFloat()); @@ -132,6 +134,7 @@ void KartRewinder::rewindToState(BareNetworkString *buffer) // 5) Max speed info // ------------------ m_max_speed->rewindTo(buffer); + m_max_speed->update(0); // 6) Skidding // ----------- From 1651d28a4c933488707d75281107984d977723b3 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 21 Sep 2016 17:31:24 +1000 Subject: [PATCH 216/350] In case of history replay update dt earlier so that World is updated with the replay dt value. --- src/modes/world.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/modes/world.cpp b/src/modes/world.cpp index a87a5d11b..fdf2c438f 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -991,6 +991,11 @@ void World::update(float dt) assert(m_magic_number == 0xB01D6543); #endif + history->update(dt); + if (history->replayHistory()) + { + dt = history->getNextDelta(); + } PROFILER_PUSH_CPU_MARKER("World::update()", 0x00, 0x7F, 0x00); @@ -1004,8 +1009,10 @@ void World::update(float dt) } #endif + PROFILER_PUSH_CPU_MARKER("World::update (sub-updates)", 0x20, 0x7F, 0x00); WorldStatus::update(dt); RewindManager::get()->saveStates(); + PROFILER_POP_CPU_MARKER(); PROFILER_PUSH_CPU_MARKER("World::update (Kart::upate)", 0x40, 0x7F, 0x00); @@ -1027,12 +1034,8 @@ void World::update(float dt) } PROFILER_POP_CPU_MARKER(); - PROFILER_PUSH_CPU_MARKER("World::update (sub-updates)", 0x20, 0x7F, 0x00); - history->update(dt); if(race_manager->isRecordingRace()) ReplayRecorder::get()->update(dt); - if(history->replayHistory()) dt=history->getNextDelta(); if (m_script_engine) m_script_engine->update(dt); - PROFILER_POP_CPU_MARKER(); if (!history->dontDoPhysics()) { From f0b5f41fc779cd803194eb4c65c0862009596ffd Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 21 Sep 2016 17:33:37 +1000 Subject: [PATCH 217/350] Added info message in case of rewinding. --- src/network/rewind_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/rewind_manager.cpp b/src/network/rewind_manager.cpp index 8ff236e01..5e8504770 100644 --- a/src/network/rewind_manager.cpp +++ b/src/network/rewind_manager.cpp @@ -278,7 +278,7 @@ void RewindManager::rewindTo(float rewind_time) { assert(!m_is_rewinding); m_is_rewinding = true; - + Log::info("rewind", "Rewinding to %f", rewind_time); history->doReplayHistory(History::HISTORY_NONE); // First find the state to which we need to rewind From 25a457f88f74b1431dad58bf26021aa1a52ea6f6 Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 22 Sep 2016 08:57:37 +1000 Subject: [PATCH 218/350] Moved m_speed computation, so that the correct speed was computed when the engine force (which is speed dependent) is used. --- src/karts/kart.cpp | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 7e6414a70..5ec5c1701 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1201,6 +1201,17 @@ void Kart::update(float dt) // Update the position and other data taken from the physics Moveable::update(dt); + // Compute the speed of the kart. Smooth it with previous speed to make + // the camera smoother (because of capping the speed in m_max_speed + // the speed value jitters when approaching maximum speed. This results + // in the distance between kart and camera to jitter as well (typically + // only in the order of centimetres though). Smoothing the speed value + // gets rid of this jitter, and also r + float old_speed = m_speed; + m_speed = getVehicle()->getRigidBody()->getLinearVelocity().length(); + float f=0.3f; + m_speed = f*m_speed + (1.0f-f)*old_speed; + if(!history->replayHistory() && !RewindManager::get()->isRewinding()) m_controller->update(dt); @@ -1209,13 +1220,19 @@ void Kart::update(float dt) #ifdef DEBUG_TO_COMPARE_KART_PHYSICS // This information is useful when comparing kart physics, e.g. to // see top speed, acceleration (i.e. time to top speed) etc. - Log::verbose("physics", "%s t %f %f xyz %f %f %f v %f %f %f s %f a %f", + Log::verbose("physics", "%s t %f %f xyz %f %f %f v %f %f %f sk %f %d %f %f %f st %f %f", getIdent().c_str(), World::getWorld()->getTime(), dt, getXYZ().getX(), getXYZ().getY(), getXYZ().getZ(), getVelocity().getX(), getVelocity().getY(), getVelocity().getZ(), - getControls().getSteer(), - getControls().getAccel()); + m_skidding->getSkidFactor(), + m_skidding->getSkidState(), + m_skidding->getSteeringFraction(), + getMaxSteerAngle(), + m_speed, + m_vehicle->getWheelInfo(0).m_steering, + m_vehicle->getWheelInfo(1).m_steering + ); #endif // if its view is blocked by plunger, decrease remaining time @@ -2173,17 +2190,6 @@ void Kart::updatePhysics(float dt) m_max_speed->setMinSpeed(min_speed); m_max_speed->update(dt); - // Compute the speed of the kart. Smooth it with previous speed to make - // the camera smoother (because of capping the speed in m_max_speed - // the speed value jitters when approaching maximum speed. This results - // in the distance between kart and camera to jitter as well (typically - // only in the order of centimetres though). Smoothing the speed value - // gets rid of this jitter, and also r - float old_speed = m_speed; - m_speed = getVehicle()->getRigidBody()->getLinearVelocity().length(); - float f=0.3f; - m_speed = f*m_speed + (1.0f-f)*old_speed; - // calculate direction of m_speed const btTransform& chassisTrans = getVehicle()->getChassisWorldTransform(); btVector3 forwardW ( From 111fc297d578e5e9162ba1927e378d2fde5ed1a8 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 23 Sep 2016 00:15:04 +0800 Subject: [PATCH 219/350] Fix overworld crash --- src/karts/kart.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index ae049374e..a377e0b66 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1296,10 +1296,11 @@ void Kart::update(float dt) // To be used later float dist_to_sector = 0.0f; - if (DriveGraph::get()) + LinearWorld* lw = dynamic_cast(World::getWorld()); + if (lw && DriveGraph::get()) { - const int sector = ((LinearWorld*)World::getWorld()) - ->getTrackSector(getWorldKartId()).getCurrentGraphNode(); + const int sector = + lw->getTrackSector(getWorldKartId()).getCurrentGraphNode(); dist_to_sector = getXYZ().distance (DriveGraph::get()->getNode(sector)->getCenter()); From 74f521980a6fec42767d7821670e3dccfea2a940 Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 23 Sep 2016 07:56:21 +1000 Subject: [PATCH 220/350] Fixed time handling to be in synch between history and rewind. --- src/main_loop.cpp | 11 ++++++++++- src/modes/world.cpp | 11 +++++------ src/race/history.cpp | 18 +++--------------- src/race/history.hpp | 11 +++-------- 4 files changed, 21 insertions(+), 30 deletions(-) diff --git a/src/main_loop.cpp b/src/main_loop.cpp index 13b57950b..82099ab0b 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -35,6 +35,7 @@ #include "network/race_event_manager.hpp" #include "network/stk_host.hpp" #include "online/request_manager.hpp" +#include "race/history.hpp" #include "race/race_manager.hpp" #include "states_screens/state_manager.hpp" #include "utils/profiler.hpp" @@ -61,6 +62,15 @@ MainLoop::~MainLoop() */ float MainLoop::getLimitedDt() { + float dt = 0; + // If we are doing a replay, use the dt from the history file + if (World::getWorld() && history->replayHistory() ) + { + dt = history->updateReplayAndGetDT(); + return dt; + } + + // In profile mode without graphics, run with a fixed dt of 1/60 if ((ProfileWorld::isProfileMode() && ProfileWorld::isNoGraphics()) || UserConfigParams::m_arena_ai_stats) @@ -71,7 +81,6 @@ float MainLoop::getLimitedDt() IrrlichtDevice* device = irr_driver->getDevice(); m_prev_time = m_curr_time; - float dt; // needed outside of the while loop while( 1 ) { m_curr_time = device->getTimer()->getRealTime(); diff --git a/src/modes/world.cpp b/src/modes/world.cpp index fdf2c438f..be678e095 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -890,6 +890,11 @@ void World::updateWorld(float dt) getPhase() == IN_GAME_MENU_PHASE ) return; + if (!history->replayHistory()) + { + history->updateSaving(dt); // updating the saved state + } + try { update(dt); @@ -991,12 +996,6 @@ void World::update(float dt) assert(m_magic_number == 0xB01D6543); #endif - history->update(dt); - if (history->replayHistory()) - { - dt = history->getNextDelta(); - } - PROFILER_PUSH_CPU_MARKER("World::update()", 0x00, 0x7F, 0x00); #if MEASURE_FPS diff --git a/src/race/history.cpp b/src/race/history.cpp index b3d5d479d..85ce6492a 100644 --- a/src/race/history.cpp +++ b/src/race/history.cpp @@ -75,19 +75,6 @@ void History::allocateMemory(int number_of_frames) m_all_rotations.resize(number_of_frames*num_karts); } // allocateMemory -//----------------------------------------------------------------------------- -/** Depending on mode either saves the data for the current time step, or - * replays the data. - * /param dt Time step. - */ -void History::update(float dt) -{ - if(m_replay_mode==HISTORY_NONE) - updateSaving(dt); - else - updateReplay(dt); -} // update - //----------------------------------------------------------------------------- /** Saves the current history. * \param dt Time step size. @@ -124,7 +111,7 @@ void History::updateSaving(float dt) /** Sets the kart position and controls to the recorded history value. * \param dt Time step size. */ -void History::updateReplay(float dt) +float History::updateReplayAndGetDT() { m_current++; World *world = World::getWorld(); @@ -159,7 +146,8 @@ void History::updateReplay(float dt) kart->getControls().set(m_all_controls[index]); } } -} // updateReplay + return m_all_deltas[m_current]; +} // updateReplayAndGetDT //----------------------------------------------------------------------------- /** Saves the history stored in the internal data structures into a file called diff --git a/src/race/history.hpp b/src/race/history.hpp index 62a13c463..55b2294f5 100644 --- a/src/race/history.hpp +++ b/src/race/history.hpp @@ -77,26 +77,21 @@ private: std::vector m_kart_ident; void allocateMemory(int number_of_frames); - void updateSaving(float dt); - void updateReplay(float dt); public: History (); void startReplay (); void initRecording (); - void update (float dt); void Save (); void Load (); + void updateSaving(float dt); + float updateReplayAndGetDT(); // -------------------I----------------------------------------------------- /** Returns the identifier of the n-th kart. */ const std::string& getKartIdent(unsigned int n) { return m_kart_ident[n]; - } - // ------------------------------------------------------------------------ - /** Returns the size of the next timestep. */ - float getNextDelta () const { return m_all_deltas[m_current]; } - + } // getKartIdent // ------------------------------------------------------------------------ /** Returns if a history is replayed, i.e. the history mode is not none. */ bool replayHistory () const { return m_replay_mode != HISTORY_NONE; } From 1a3daafbff399d9be40b42c5fde1bae731896c93 Mon Sep 17 00:00:00 2001 From: deve Date: Fri, 23 Sep 2016 10:22:56 +0200 Subject: [PATCH 221/350] Fixed exit on startup when force legacy device is set in graphics restrictions. It was working on Linux, but on Windows we need to clear system messages when closing irrlicht device. --- src/graphics/irr_driver.cpp | 64 +++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 048f7b88c..516613380 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -355,7 +355,7 @@ void IrrDriver::createListOfVideoModes() void IrrDriver::initDevice() { SIrrlichtCreationParameters params; - + // If --no-graphics option was used, the null device can still be used. if (!ProfileWorld::isNoGraphics()) { @@ -438,7 +438,7 @@ void IrrDriver::initDevice() m_device->drop(); m_device = NULL; - params.ForceLegacyDevice = (UserConfigParams::m_force_legacy_device || + params.ForceLegacyDevice = (UserConfigParams::m_force_legacy_device || UserConfigParams::m_gamepad_visualisation); // Try 32 and, upon failure, 24 then 16 bit per pixels @@ -463,9 +463,9 @@ void IrrDriver::initDevice() core::dimension2du(UserConfigParams::m_width, UserConfigParams::m_height); params.HandleSRGB = true; - params.ShadersPath = (file_manager->getShadersDir() + + params.ShadersPath = (file_manager->getShadersDir() + "irrlicht/").c_str(); - + /* switch ((int)UserConfigParams::m_antialiasing) { @@ -526,13 +526,13 @@ void IrrDriver::initDevice() { Log::fatal("irr_driver", "Couldn't initialise irrlicht device. Quitting.\n"); } - + CVS->init(); - + bool recreate_device = false; - + // Some drivers are able to create OpenGL 3.1 context, but shader-based - // pipeline doesn't work for them. For example some radeon drivers + // pipeline doesn't work for them. For example some radeon drivers // support only GLSL 1.3 and it causes STK to crash. We should force to use // fixed pipeline in this case. if (!ProfileWorld::isNoGraphics() && @@ -542,13 +542,13 @@ void IrrDriver::initDevice() "Re-creating device to workaround the issue."); params.ForceLegacyDevice = true; - recreate_device = true; + recreate_device = true; } - + // This is the ugly hack for intel driver on linux, which doesn't // use sRGB-capable visual, even if we request it. This causes - // the screen to be darker than expected. It affects mesa 10.6 and newer. - // Though we are able to force to use the proper format on mesa side by + // the screen to be darker than expected. It affects mesa 10.6 and newer. + // Though we are able to force to use the proper format on mesa side by // setting WithAlphaChannel parameter. else if (CVS->needsSRGBCapableVisualWorkaround()) { @@ -558,19 +558,21 @@ void IrrDriver::initDevice() params.WithAlphaChannel = true; recreate_device = true; } - + if (!ProfileWorld::isNoGraphics() && recreate_device) { m_device->closeDevice(); - m_device->drop(); - + m_device->clearSystemMessages(); + m_device->run(); + m_device->drop(); + m_device = createDeviceEx(params); - + if(!m_device) { Log::fatal("irr_driver", "Couldn't initialise irrlicht device. Quitting.\n"); } - + CVS->init(); } @@ -587,7 +589,7 @@ void IrrDriver::initDevice() (UserConfigParams::m_shadows_resolution < 512 || UserConfigParams::m_shadows_resolution > 2048)) { - Log::warn("irr_driver", + Log::warn("irr_driver", "Invalid value for UserConfigParams::m_shadows_resolution : %i", (int)UserConfigParams::m_shadows_resolution); UserConfigParams::m_shadows_resolution = 0; @@ -697,7 +699,7 @@ void IrrDriver::setMaxTextureSize() { io::IAttributes &att = m_video_driver->getNonConstDriverAttributes(); att.setAttribute("MAX_TEXTURE_SIZE", core::dimension2du( - UserConfigParams::m_max_texture_size, + UserConfigParams::m_max_texture_size, UserConfigParams::m_max_texture_size)); } } // setMaxTextureSize @@ -725,7 +727,7 @@ void IrrDriver::createSunInterposer() mb->getMaterial().setTexture(7, getUnicolorTexture(video::SColor(0, 0, 0, 0))); } - m_sun_interposer = new STKMeshSceneNode(sphere, + m_sun_interposer = new STKMeshSceneNode(sphere, m_scene_manager->getRootSceneNode(), NULL, -1, "sun_interposer"); @@ -1176,7 +1178,7 @@ scene::IMeshSceneNode *IrrDriver::addSphere(float radius, { scene::IMesh *mesh = m_scene_manager->getGeometryCreator() ->createSphereMesh(radius); - + mesh->setMaterialFlag(video::EMF_COLOR_MATERIAL, true); video::SMaterial &m = mesh->getMeshBuffer(0)->getMaterial(); m.AmbientColor = color; @@ -1227,7 +1229,7 @@ scene::IMeshSceneNode *IrrDriver::addMesh(scene::IMesh *mesh, if (!parent) parent = m_scene_manager->getRootSceneNode(); - scene::IMeshSceneNode* node = new STKMeshSceneNode(mesh, parent, + scene::IMeshSceneNode* node = new STKMeshSceneNode(mesh, parent, m_scene_manager, -1, debug_name, core::vector3df(0, 0, 0), @@ -1266,7 +1268,7 @@ scene::ISceneNode *IrrDriver::addBillboard(const core::dimension2d< f32 > size, if (!parent) parent = m_scene_manager->getRootSceneNode(); - node = new STKBillboard(parent, m_scene_manager, -1, + node = new STKBillboard(parent, m_scene_manager, -1, vector3df(0., 0., 0.), size); node->drop(); } @@ -1487,7 +1489,7 @@ scene::ISceneNode *IrrDriver::addSkyBox(const std::vector &tex { m_spherical_harmonics->setTextures(spherical_harmonics_textures); } - + return m_scene_manager->addSkyBoxSceneNode(texture[0], texture[1], texture[2], texture[3], texture[4], texture[5]); @@ -2249,13 +2251,13 @@ void IrrDriver::update(float dt) renderGLSL(dt); else renderFixed(dt); - + GUIEngine::Screen* current_screen = GUIEngine::getCurrentScreen(); if (current_screen != NULL && current_screen->needs3D()) { GUIEngine::render(dt); } - + if (world->getPhysics() != NULL) { IrrDebugDrawer* debug_drawer = world->getPhysics()->getDebugDrawer(); @@ -2274,7 +2276,7 @@ void IrrDriver::update(float dt) m_video_driver->endScene(); } - + if (m_request_screenshot) doScreenShot(); // Enable this next print statement to get render information printed @@ -2448,7 +2450,7 @@ void IrrDriver::RTTProvider::setupRTTScene(PtrVector& mesh, } irr_driver->getSceneManager()->setAmbientLight(video::SColor(255, 35, 35, 35) ); - + const core::vector3df &spot_pos = core::vector3df(0, 30, 40); m_light = irr_driver->getSceneManager() ->addLightSceneNode(NULL, spot_pos, video::SColorf(1.0f,1.0f,1.0f), @@ -2564,9 +2566,9 @@ void IrrDriver::applyObjectPassShader(scene::ISceneNode * const node, bool rimli const u32 mcount = node->getMaterialCount(); u32 i; - const video::E_MATERIAL_TYPE ref = + const video::E_MATERIAL_TYPE ref = Shaders::getShader(rimlit ? ES_OBJECTPASS_RIMLIT : ES_OBJECTPASS_REF); - const video::E_MATERIAL_TYPE pass = + const video::E_MATERIAL_TYPE pass = Shaders::getShader(rimlit ? ES_OBJECTPASS_RIMLIT : ES_OBJECTPASS); const video::E_MATERIAL_TYPE origref = Shaders::getShader(ES_OBJECTPASS_REF); @@ -2593,7 +2595,7 @@ void IrrDriver::applyObjectPassShader(scene::ISceneNode * const node, bool rimli for (i = 0; i < mcount; i++) { video::SMaterial &nodemat = node->getMaterial(i); - video::SMaterial &mbmat = mesh ? mesh->getMeshBuffer(i)->getMaterial() + video::SMaterial &mbmat = mesh ? mesh->getMeshBuffer(i)->getMaterial() : nodemat; video::SMaterial *mat = &nodemat; From 123e667ab4287e1f92ccdef0275f836bbffd7365 Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 24 Sep 2016 10:05:15 +0800 Subject: [PATCH 222/350] Simplify code --- src/tracks/arena_node_3d.hpp | 6 ++---- src/tracks/bounding_box_3d.hpp | 31 ++++++++++++++++--------------- src/tracks/drive_node_3d.cpp | 3 +-- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/tracks/arena_node_3d.hpp b/src/tracks/arena_node_3d.hpp index e4b19edeb..2ea8cfec3 100644 --- a/src/tracks/arena_node_3d.hpp +++ b/src/tracks/arena_node_3d.hpp @@ -31,10 +31,8 @@ class ArenaNode3D : public ArenaNode, public: ArenaNode3D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, const Vec3 &normal, unsigned int node_index) - : ArenaNode(p0, p1, p2, p3, normal, node_index) - { - BoundingBox3D::init(p0, p1, p2, p3, normal); - } + : ArenaNode(p0, p1, p2, p3, normal, node_index), + BoundingBox3D(p0, p1, p2, p3, normal) {} // ------------------------------------------------------------------------ virtual bool pointInside(const Vec3& p, bool ignore_vertical = false) const OVERRIDE diff --git a/src/tracks/bounding_box_3d.hpp b/src/tracks/bounding_box_3d.hpp index 4f4c5ebd6..a2b596007 100644 --- a/src/tracks/bounding_box_3d.hpp +++ b/src/tracks/bounding_box_3d.hpp @@ -33,21 +33,8 @@ private: public: // ------------------------------------------------------------------------ - bool pointInside(const Vec3& p) const - { - float side = p.sideofPlane(m_box_faces[0][0], m_box_faces[0][1], - m_box_faces[0][2]); - for (int i = 1; i < 6; i++) - { - if (side * p.sideofPlane(m_box_faces[i][0], m_box_faces[i][1], - m_box_faces[i][2]) < 0) - return false; - } - return true; - } - // ------------------------------------------------------------------------ - void init(const Vec3& p0, const Vec3& p1, const Vec3& p2, const Vec3& p3, - const Vec3& normal) + BoundingBox3D(const Vec3& p0, const Vec3& p1, const Vec3& p2, + const Vec3& p3, const Vec3& normal) { // Compute the node bounding box used by pointInside Vec3 box_corners[8]; @@ -78,6 +65,20 @@ public: m_box_faces[i][j] = box_faces[i][j]; } } + // ------------------------------------------------------------------------ + bool pointInside(const Vec3& p, bool ignore_vertical = false) const + { + float side = p.sideofPlane(m_box_faces[0][0], m_box_faces[0][1], + m_box_faces[0][2]); + for (int i = 1; i < 6; i++) + { + if (side * p.sideofPlane(m_box_faces[i][0], m_box_faces[i][1], + m_box_faces[i][2]) < 0) + return false; + } + return true; + } + }; #endif \ No newline at end of file diff --git a/src/tracks/drive_node_3d.cpp b/src/tracks/drive_node_3d.cpp index be8f83752..133d2c159 100644 --- a/src/tracks/drive_node_3d.cpp +++ b/src/tracks/drive_node_3d.cpp @@ -24,9 +24,8 @@ DriveNode3D::DriveNode3D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, unsigned int node_index, bool invisible, bool ai_ignore) : DriveNode(p0, p1, p2, p3, normal, node_index, invisible, - ai_ignore) + ai_ignore), BoundingBox3D(p0, p1, p2, p3, normal) { - BoundingBox3D::init(p0, p1, p2, p3, normal); m_line = core::line3df(m_lower_center.toIrrVector(), m_upper_center.toIrrVector()); } // DriveNode3D From bff7154e0375ccb50a6e71f809b9eb6bdec52cfb Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 25 Sep 2016 11:22:15 +0800 Subject: [PATCH 223/350] Simplify code and fix typo Because AI always aims quad center now even for 3d quads --- src/karts/controller/skidding_ai.cpp | 46 +++++++--------------------- 1 file changed, 11 insertions(+), 35 deletions(-) diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index e370cea0a..057132d54 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -779,14 +779,8 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, { // Kart will not hit item, try to get closer to this item // so that it can potentially become a permanent target. - Vec3 xyz = item_to_collect->getXYZ(); - Vec3 item_direction = xyz - m_kart->getXYZ(); - Vec3 plane_normal = DriveGraph::get()->getNode(m_track_node) - ->getNormal(); - float dist_to_plane = item_direction.dot(plane_normal); - Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; - - float angle_to_item = (projected_xyz - m_kart->getXYZ()) + const Vec3& xyz = item_to_collect->getXYZ(); + float angle_to_item = (xyz - m_kart->getXYZ()) .angle(kart_aim_direction); float angle = normalizeAngle(angle_to_item); @@ -859,19 +853,8 @@ bool SkiddingAI::handleSelectedItem(Vec3 kart_aim_direction, Vec3 *aim_point) if(m_item_to_collect->getDisableTime()>0) return false; - // Project the item's location onto the plane of the current quad. - // This is necessary because the kart's aim point may not be on the track - // in 3D curves. So we project the item's location onto the plane in which - // the kart is. The current quad provides a good estimate of the kart's plane. const Vec3 &xyz = m_item_to_collect->getXYZ(); - Vec3 item_direction = xyz - m_kart->getXYZ(); - Vec3 plane_normal = DriveGraph::get()->getNode(m_track_node)->getNormal(); - float dist_to_plane = item_direction.dot(plane_normal); - Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; - - float angle_to_item = (projected_xyz - m_kart->getXYZ()) - .angle(kart_aim_direction); - + float angle_to_item = (xyz - m_kart->getXYZ()).angle(kart_aim_direction); float angle = normalizeAngle(angle_to_item); if(fabsf(angle)>1.5) @@ -924,9 +907,9 @@ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, // Check if we would drive left of the leftmost or right of the // rightmost point - if so, nothing to do. - Vec3 left(items_to_avoid[index_left_most]->getXYZ()); + const Vec3& left = items_to_avoid[index_left_most]->getXYZ(); int node_index = items_to_avoid[index_left_most]->getGraphNode(); - Vec3 normal = DriveGraph::get()->getNode(node_index)->getNormal(); + const Vec3& normal = DriveGraph::get()->getNode(node_index)->getNormal(); Vec3 p1 = line_to_target.start, p2 = line_to_target.getMiddle() + normal.toIrrVector(), p3 = line_to_target.end; @@ -944,14 +927,14 @@ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, } else { - Vec3 left(items_to_avoid[index_left_most]->getXYZ()); - int node_index = items_to_avoid[index_left_most]->getGraphNode(); - Vec3 normal = DriveGraph::get()->getNode(node_index)->getNormal(); + const Vec3& right = items_to_avoid[index_right_most]->getXYZ(); + int node_index = items_to_avoid[index_right_most]->getGraphNode(); + const Vec3& normal = DriveGraph::get()->getNode(node_index)->getNormal(); Vec3 p1 = line_to_target.start, p2 = line_to_target.getMiddle() + normal.toIrrVector(), p3 = line_to_target.end; - if (left.sideofPlane(p1, p2, p3) >= 0) + if (right.sideofPlane(p1, p2, p3) >= 0) { // Right of rightmost point item_index = index_right_most; @@ -996,7 +979,6 @@ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, for(unsigned int i=0; igetXYZ(); - core::vector2df item2d = xyz.toIrrVector2d(); core::vector3df point3d = line_to_target.getClosestPoint(xyz.toIrrVector()); float d = (xyz.toIrrVector() - point3d).getLengthSQ(); float direction = xyz.sideofPlane(p1,p2,p3); @@ -1103,14 +1085,8 @@ void SkiddingAI::evaluateItems(const Item *item, Vec3 kart_aim_direction, // in 3D curves. So we project the item's location onto the plane in which // the kart is. The current quad provides a good estimate of the kart's plane. const Vec3 &xyz = item->getXYZ(); - Vec3 item_direction = xyz - m_kart->getXYZ(); - Vec3 plane_normal = DriveGraph::get()->getNode(m_track_node)->getNormal(); - float dist_to_plane = item_direction.dot(plane_normal); - Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; - - float angle_to_item = (projected_xyz - m_kart->getXYZ()) - .angle(kart_aim_direction); - + float angle_to_item = + (xyz - m_kart->getXYZ()).angle(kart_aim_direction); float diff = normalizeAngle(angle_to_item); // The kart is driving at high speed, when the current max speed From 3659ad9068d5cc862d14bc236205da305f0b59a4 Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 26 Sep 2016 00:11:49 +0800 Subject: [PATCH 224/350] Try to make determineTurnRadius works with any plane --- src/karts/controller/skidding_ai.cpp | 95 +++++++++++++--------------- src/karts/controller/skidding_ai.hpp | 6 +- 2 files changed, 44 insertions(+), 57 deletions(-) diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index 057132d54..a96105e7c 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -173,6 +173,7 @@ void SkiddingAI::reset() m_curve_center = Vec3(0,0,0); m_current_track_direction = DriveNode::DIR_STRAIGHT; m_item_to_collect = NULL; + m_last_direction_node = 0; m_avoid_item_close = false; m_skid_probability_state = SKID_PROBAB_NOT_YET; m_last_item_random = NULL; @@ -2112,20 +2113,17 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) */ void SkiddingAI::determineTrackDirection() { - const DriveGraph *qg = DriveGraph::get(); - unsigned int succ = m_successor_index[m_track_node]; - unsigned int next = qg->getNode(m_track_node)->getSuccessor(succ); - - //float angle_to_track = qg->getNode(m_track_node)->getAngleToSuccessor(succ) - // - m_kart->getHeading(); - Vec3 track_direction = -qg->getNode(m_track_node)->getCenter() - + qg->getNode(next)->getCenter(); - //Vec3 kart_direction = qg->getNode(m_track_node)->getCenter() + m_kart->getVelocity(); - - float angle_to_track = 0; + const DriveGraph *dg = DriveGraph::get(); + unsigned int succ = m_successor_index[m_track_node]; + unsigned int next = dg->getNode(m_track_node)->getSuccessor(succ); + float angle_to_track = 0.0f; if (m_kart->getVelocity().length() > 0.0f) - angle_to_track = track_direction.angle(m_kart->getVelocity().normalized()); - + { + Vec3 track_direction = -dg->getNode(m_track_node)->getCenter() + + dg->getNode(next)->getCenter(); + angle_to_track = + track_direction.angle(m_kart->getVelocity().normalized()); + } angle_to_track = normalizeAngle(angle_to_track); // In certain circumstances (esp. S curves) it is possible that the @@ -2145,7 +2143,7 @@ void SkiddingAI::determineTrackDirection() return; } - qg->getNode(next)->getDirectionData(m_successor_index[next], + dg->getNode(next)->getDirectionData(m_successor_index[next], &m_current_track_direction, &m_last_direction_node); @@ -2153,7 +2151,7 @@ void SkiddingAI::determineTrackDirection() m_curve[CURVE_QG]->clear(); for(unsigned int i=m_track_node; i<=m_last_direction_node; i++) { - m_curve[CURVE_QG]->addPoint(qg->getNode(i)->getCenter()); + m_curve[CURVE_QG]->addPoint(dg->getNode(i)->getCenter()); } #endif @@ -2183,13 +2181,10 @@ void SkiddingAI::handleCurve() // kart will already point towards the direction of the circle), and // the case that the kart is facing wrong was already tested for before - const DriveGraph *qg = DriveGraph::get(); - Vec3 xyz = m_kart->getXYZ(); - Vec3 tangent = m_kart->getTrans()(Vec3(0,0,1)) - xyz; - Vec3 last_xyz = qg->getNode(m_last_direction_node)->getCenter(); + const DriveGraph *dg = DriveGraph::get(); + const Vec3& last_xyz = dg->getNode(m_last_direction_node)->getCenter(); - determineTurnRadius(xyz, tangent, last_xyz, - &m_curve_center, &m_current_curve_radius); + determineTurnRadius(last_xyz, &m_curve_center, &m_current_curve_radius); assert(!std::isnan(m_curve_center.getX())); assert(!std::isnan(m_curve_center.getY())); assert(!std::isnan(m_curve_center.getZ())); @@ -2205,11 +2200,12 @@ void SkiddingAI::handleCurve() // Pick either the lower left or right point: int index = m_current_track_direction==DriveNode::DIR_LEFT ? 0 : 1; - float r = (m_curve_center - *(qg->getNode(i))[index]).length(); + Vec3 curve_center_wc = m_kart->getTrans()(m_curve_center); + float r = (curve_center_wc - *(dg->getNode(i))[index]).length(); if(m_current_curve_radius < r) { - last_xyz = *(qg->getNode(i)[index]); - determineTurnRadius(xyz, tangent, last_xyz, + last_xyz = *(dg->getNode(i)[index]); + determineTurnRadius(last_xyz, &m_curve_center, &m_current_curve_radius); m_last_direction_node = i; break; @@ -2219,10 +2215,10 @@ void SkiddingAI::handleCurve() } #endif #if defined(AI_DEBUG) && defined(AI_DEBUG_CIRCLES) - m_curve[CURVE_PREDICT1]->makeCircle(m_curve_center, + m_curve[CURVE_PREDICT1]->makeCircle(m_kart->getTrans()(m_curve_center), m_current_curve_radius); m_curve[CURVE_PREDICT1]->addPoint(last_xyz); - m_curve[CURVE_PREDICT1]->addPoint(m_curve_center); + m_curve[CURVE_PREDICT1]->addPoint(m_kart->getTrans()(m_curve_center)); m_curve[CURVE_PREDICT1]->addPoint(xyz); #endif @@ -2277,15 +2273,16 @@ bool SkiddingAI::canSkid(float steer_fraction) } const float MIN_SKID_SPEED = 5.0f; - const DriveGraph *qg = DriveGraph::get(); - Vec3 last_xyz = qg->getNode(m_last_direction_node)->getCenter(); + const DriveGraph *dg = DriveGraph::get(); + Vec3 last_xyz = m_kart->getTrans().inverse() + (dg->getNode(m_last_direction_node)->getCenter()); // Only try skidding when a certain minimum speed is reached. if(m_kart->getSpeed()getXYZ() - m_curve_center; - Vec3 diff_last = last_xyz - m_curve_center; + Vec3 diff_kart = -m_curve_center; + Vec3 diff_last = last_xyz - m_curve_center; float angle_kart = atan2(diff_kart.getX(), diff_kart.getZ()); float angle_last = atan2(diff_last.getX(), diff_last.getZ()); float angle = m_current_track_direction == DriveNode::DIR_RIGHT @@ -2472,52 +2469,46 @@ void SkiddingAI::setSteering(float angle, float dt) // ---------------------------------------------------------------------------- /** Determine the center point and radius of a circle given two points on - * the ccircle and the tangent at the first point. This is done as follows: + * the circle and the tangent at the first point. This is done as follows: * 1. Determine the line going through the center point start+end, which is * orthogonal to the vector from start to end. This line goes through the * center of the circle. * 2. Determine the line going through the first point and is orthogonal * to the given tangent. * 3. The intersection of these two lines is the center of the circle. - * \param[in] start First point. - * \param[in] tangent Tangent at first point. * \param[in] end Second point on circle. - * \param[out] center Center point of the circle. + * \param[out] center Center point of the circle (local coordinate). * \param[out] radius Radius of the circle. */ -void SkiddingAI::determineTurnRadius(const Vec3 &start, - const Vec3 &tangent, - const Vec3 &end, - Vec3 *center, - float *radius) +void SkiddingAI::determineTurnRadius(const Vec3 &end, Vec3 *center, + float *radius) { + // Convert end point to local coordinate, so start will be 0, 0, 0 + Vec3 lc = m_kart->getTrans().inverse()(end); + // 1) Line through middle of start+end - Vec3 mid = 0.5f*(start+end); - Vec3 direction = end-start; + Vec3 mid = 0.5f * lc; + Vec3 direction = lc; Vec3 orthogonal(direction.getZ(), 0, -direction.getX()); - Vec3 q1 = mid + orthogonal; + Vec3 q1 = mid + orthogonal; irr::core::line2df line1(mid.getX(), mid.getZ(), q1.getX(), q1.getZ() ); - Vec3 ortho_tangent(tangent.getZ(), 0, -tangent.getX()); - Vec3 q2 = start + ortho_tangent; - irr::core::line2df line2(start.getX(), start.getZ(), - q2.getX(), q2.getZ()); - - + irr::core::line2df line2(0, 0, 1, 0); irr::core::vector2df result; if(line1.intersectWith(line2, result, /*checkOnlySegments*/false)) { - *center = Vec3(result.X, start.getY(), result.Y); - *radius = (start - *center).length(); + Vec3 lc_center(result.X, 0, result.Y); + *center = lc_center; + *radius = lc_center.length(); } else { // No intersection. In this case assume that the two points are // on a semicircle, in which case the center is at 0.5*(start+end): - *center = 0.5f*(start+end); - *radius = 0.5f*(end-start).length(); + *center = mid; + *radius = 0.5f*(lc).length(); } return; } // determineTurnRadius diff --git a/src/karts/controller/skidding_ai.hpp b/src/karts/controller/skidding_ai.hpp index 503755e05..72a348631 100644 --- a/src/karts/controller/skidding_ai.hpp +++ b/src/karts/controller/skidding_ai.hpp @@ -265,11 +265,7 @@ private: void findNonCrashingPoint(Vec3 *result, int *last_node); void determineTrackDirection(); - void determineTurnRadius(const Vec3 &start, - const Vec3 &start_direction, - const Vec3 &end, - Vec3 *center, - float *radius); + void determineTurnRadius(const Vec3 &end, Vec3 *center, float *radius); virtual bool canSkid(float steer_fraction); virtual void setSteering(float angle, float dt); void handleCurve(); From a6568e01eacc837a18fb942c00290ea98b6ed7ba Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 26 Sep 2016 09:01:18 +1000 Subject: [PATCH 225/350] Propperly handle instand speed increases in the physics when rewinding. --- src/karts/kart.cpp | 3 +++ src/karts/kart_rewinder.cpp | 5 ++++- src/physics/btKart.cpp | 5 +++++ src/physics/btKart.hpp | 4 +++- 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 5ec5c1701..ec9121082 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1168,6 +1168,9 @@ void Kart::eliminate() */ void Kart::update(float dt) { + // Reset any instand speed increase in the bullet kart + m_vehicle->resetInstantSpeed(); + // update star effect (call will do nothing if stars are not activated) m_stars_effect->update(dt); diff --git a/src/karts/kart_rewinder.cpp b/src/karts/kart_rewinder.cpp index 8bd419959..340424213 100644 --- a/src/karts/kart_rewinder.cpp +++ b/src/karts/kart_rewinder.cpp @@ -74,7 +74,7 @@ BareNetworkString* KartRewinder::saveState() const buffer->add(body->getLinearVelocity()); buffer->add(body->getAngularVelocity()); buffer->addUInt8(m_has_started); // necessary for startup speed boost - buffer->addFloat(m_vehicle->getZipperSpeed()); + buffer->addFloat(m_vehicle->getInstantSpeedIncrease()); // 2) Steering and other player controls // ------------------------------------- @@ -116,6 +116,9 @@ void KartRewinder::rewindToState(BareNetworkString *buffer) // This function also reads the velocity, so it must be called // after the velocities are set body->proceedToTransform(t); + // Update kart transform in case that there are access to its value + // before Moveable::update() is called (which updates the transform) + setTrans(t); m_has_started = buffer->getUInt8()!=0; // necessary for startup speed boost m_vehicle->instantSpeedIncreaseTo(buffer->getFloat()); diff --git a/src/physics/btKart.cpp b/src/physics/btKart.cpp index e34549d1a..446c1daab 100644 --- a/src/physics/btKart.cpp +++ b/src/physics/btKart.cpp @@ -1062,6 +1062,11 @@ void btKart::setSliding(bool active) */ void btKart::instantSpeedIncreaseTo(float speed) { + // Avoid that a speed 'increase' might cause a slowdown + if (m_chassisBody->getLinearVelocity().length2() > speed*speed) + { + return; + } m_zipper_active = speed > 0; m_zipper_speed = speed; } // activateZipper diff --git a/src/physics/btKart.hpp b/src/physics/btKart.hpp index 05f9bc256..b81c6ed44 100644 --- a/src/physics/btKart.hpp +++ b/src/physics/btKart.hpp @@ -276,7 +276,9 @@ public: } // setTimedTorque // ------------------------------------------------------------------------ /** Returns the current zipper speed. */ - float getZipperSpeed() const { return m_zipper_speed; } + float getInstantSpeedIncrease() const { return m_zipper_speed; } + // ------------------------------------------------------------------------ + void resetInstantSpeed() { m_zipper_speed = 0; } }; // class btKart #endif //BT_KART_HPP From 81004adb9048aca46000c89f963f2a1b5e95088c Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 26 Sep 2016 09:28:28 +1000 Subject: [PATCH 226/350] Moved all m_speed handling into a seperate function. --- src/karts/kart.cpp | 90 ++++++++++++++++++++++------------------------ src/karts/kart.hpp | 1 + 2 files changed, 43 insertions(+), 48 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index ec9121082..b9edb0cd4 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1204,17 +1204,10 @@ void Kart::update(float dt) // Update the position and other data taken from the physics Moveable::update(dt); - // Compute the speed of the kart. Smooth it with previous speed to make - // the camera smoother (because of capping the speed in m_max_speed - // the speed value jitters when approaching maximum speed. This results - // in the distance between kart and camera to jitter as well (typically - // only in the order of centimetres though). Smoothing the speed value - // gets rid of this jitter, and also r - float old_speed = m_speed; - m_speed = getVehicle()->getRigidBody()->getLinearVelocity().length(); - float f=0.3f; - m_speed = f*m_speed + (1.0f-f)*old_speed; + // Update the locally maintained speed of the kart (m_speed), which + // is used furthermore for engine power, camera distance etc + updateSpeed(); if(!history->replayHistory() && !RewindManager::get()->isRewinding()) m_controller->update(dt); @@ -1512,6 +1505,45 @@ void Kart::update(float dt) } // update +//----------------------------------------------------------------------------- +/** Updates the local speed based on the current physical velocity. The value + * is smoothed exponentially to avoid camera stuttering (camera distance + * is dependent on speed) + */ +void Kart::updateSpeed() +{ + // Compute the speed of the kart. Smooth it with previous speed to make + // the camera smoother (because of capping the speed in m_max_speed + // the speed value jitters when approaching maximum speed. This results + // in the distance between kart and camera to jitter as well (typically + // only in the order of centimetres though). Smoothing the speed value + // gets rid of this jitter, and also r + float old_speed = m_speed; + m_speed = getVehicle()->getRigidBody()->getLinearVelocity().length(); + float f = 0.3f; + m_speed = f*m_speed + (1.0f - f)*old_speed; + + // calculate direction of m_speed + const btTransform& chassisTrans = getVehicle()->getChassisWorldTransform(); + btVector3 forwardW( + chassisTrans.getBasis()[0][2], + chassisTrans.getBasis()[1][2], + chassisTrans.getBasis()[2][2]); + + if (forwardW.dot(getVehicle()->getRigidBody()->getLinearVelocity()) < btScalar(0.)) + m_speed *= -1.f; + + // At low velocity, forces on kart push it back and forth so we ignore this + if (fabsf(m_speed) < 0.2f) // quick'n'dirty workaround for bug 1776883 + m_speed = 0; + + if (dynamic_cast(getKartAnimation()) || + dynamic_cast(getKartAnimation())) + { + m_speed = 0; + } +} // updateSpeed + //----------------------------------------------------------------------------- /** Show fire to go with a zipper. */ @@ -2193,44 +2225,6 @@ void Kart::updatePhysics(float dt) m_max_speed->setMinSpeed(min_speed); m_max_speed->update(dt); - // calculate direction of m_speed - const btTransform& chassisTrans = getVehicle()->getChassisWorldTransform(); - btVector3 forwardW ( - chassisTrans.getBasis()[0][2], - chassisTrans.getBasis()[1][2], - chassisTrans.getBasis()[2][2]); - - if (forwardW.dot(getVehicle()->getRigidBody()->getLinearVelocity()) < btScalar(0.)) - m_speed *= -1.f; - - // To avoid tunneling (which can happen on long falls), clamp the - // velocity in Y direction. Tunneling can happen if the Y velocity - // is larger than the maximum suspension travel (per frame), since then - // the wheel suspension can not stop/slow down the fall (though I am - // not sure if this is enough in all cases!). So the speed is limited - // to suspensionTravel / dt with dt = 1/60 (since this is the dt - // bullet is using). - - // Only apply if near ground instead of purely based on speed avoiding - // the "parachute on top" look. - const Vec3 &v = m_body->getLinearVelocity(); - if(/*isNearGround() &&*/ v.getY() < - m_kart_properties->getSuspensionTravel() * 60) - { - Vec3 v_clamped = v; - // clamp the speed to 99% of the maxium falling speed. - v_clamped.setY(-m_kart_properties->getSuspensionTravel() * 60 * 0.99f); - //m_body->setLinearVelocity(v_clamped); - } - - //at low velocity, forces on kart push it back and forth so we ignore this - if(fabsf(m_speed) < 0.2f) // quick'n'dirty workaround for bug 1776883 - m_speed = 0; - - if (dynamic_cast(getKartAnimation()) || - dynamic_cast(getKartAnimation())) - { - m_speed = 0; - } updateEngineSFX(); #ifdef XX diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index 16ef6c65f..be2bbb565 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -224,6 +224,7 @@ protected: void updateSliding(); void updateEnginePowerAndBrakes(float dt); void updateEngineSFX(); + void updateSpeed(); void updateNitro(float dt); float getActualWheelForce(); void playCrashSFX(const Material* m, AbstractKart *k); From 2701af315b2dc7f0b2de3db9805d392ff0b80230 Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 26 Sep 2016 10:09:53 +0800 Subject: [PATCH 227/350] Try to make checkCrashes work in any direction --- src/karts/controller/skidding_ai.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index a96105e7c..bc28826d5 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -1742,16 +1742,14 @@ void SkiddingAI::checkCrashes(const Vec3& pos ) const size_t NUM_KARTS = m_world->getNumKarts(); - //Protection against having vel_normal with nan values - const Vec3 &VEL = m_kart->getVelocity(); - Vec3 vel_normal(VEL.getX(), 0.0, VEL.getZ()); - float speed=vel_normal.length(); + float speed = m_kart->getVelocity().length(); // If the velocity is zero, no sense in checking for crashes in time if(speed==0) return; + Vec3 vel_normal = m_kart->getVelocity().normalized(); + // Time it takes to drive for m_kart_length units. float dt = m_kart_length / speed; - vel_normal/=speed; int current_node = m_track_node; if(steps<1 || steps>1000) @@ -1781,7 +1779,7 @@ void SkiddingAI::checkCrashes(const Vec3& pos ) continue; Vec3 other_kart_xyz = other_kart->getXYZ() + other_kart->getVelocity()*(i*dt); - float kart_distance = (step_coord - other_kart_xyz).length_2d(); + float kart_distance = (step_coord - other_kart_xyz).length(); if( kart_distance < m_kart_length) m_crashes.m_kart = j; From 9be31ba57b716c08913f5ce73f91462dec22402e Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 26 Sep 2016 10:25:38 +0800 Subject: [PATCH 228/350] Fix build --- src/karts/controller/skidding_ai.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index bc28826d5..bbdadd653 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -2217,7 +2217,7 @@ void SkiddingAI::handleCurve() m_current_curve_radius); m_curve[CURVE_PREDICT1]->addPoint(last_xyz); m_curve[CURVE_PREDICT1]->addPoint(m_kart->getTrans()(m_curve_center)); - m_curve[CURVE_PREDICT1]->addPoint(xyz); + m_curve[CURVE_PREDICT1]->addPoint(m_kart->getXYZ()); #endif } // handleCurve From 1260f23db016536bcb4f7cd6add0338b0dbb0bea Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 26 Sep 2016 11:32:19 +0800 Subject: [PATCH 229/350] Try to fix animation Don't create a new one after the previous one just got deleted --- src/karts/kart.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index a377e0b66..d0c52bf9b 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1252,7 +1252,10 @@ void Kart::update(float dt) m_kart_properties->getStabilityChassisAngularDamping()); } - if(m_kart_animation) + // Used to prevent creating a rescue animation after an explosion animation + // got deleted + const bool has_animation_before = m_kart_animation!= NULL; + if(has_animation_before) m_kart_animation->update(dt); m_attachment->update(dt); @@ -1313,7 +1316,7 @@ void Kart::update(float dt) if (World::getWorld()->getTrack()->isAutoRescueEnabled() && (!m_terrain_info->getMaterial() || !m_terrain_info->getMaterial()->hasGravity()) && - !getKartAnimation() && fabs(roll) > 60 * DEGREE_TO_RAD && + !has_animation_before && fabs(roll) > 60 * DEGREE_TO_RAD && fabs(getSpeed()) < 3.0f) { new RescueAnimation(this, /*is_auto_rescue*/true); From 6f67fd6507199500bf064877e8404725bbf2d9e9 Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 26 Sep 2016 12:46:44 +0800 Subject: [PATCH 230/350] Make swatter be able to swat in any direction --- src/items/swatter.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/items/swatter.cpp b/src/items/swatter.cpp index 51940ddc5..41803c972 100644 --- a/src/items/swatter.cpp +++ b/src/items/swatter.cpp @@ -280,13 +280,11 @@ void Swatter::pointToTarget() } else { - Vec3 swatter_to_target = m_target->getXYZ() - -Vec3(m_scene_node->getAbsolutePosition()); + Vec3 swatter_to_target = + m_kart->getTrans().inverse()(m_target->getXYZ()); float dy = -swatter_to_target.getZ(); float dx = swatter_to_target.getX(); - float angle = SWAT_ANGLE_OFFSET + (atan2(dy, dx)-m_kart->getHeading()) - * 180.0f/M_PI; - + float angle = SWAT_ANGLE_OFFSET + atan2f(dy, dx) * 180 / M_PI; m_scene_node->setRotation(core::vector3df(0.0, angle, 0.0)); } } // pointToTarget From cabc696710bdecf5c9e74a4ca16050f6b31c4f2a Mon Sep 17 00:00:00 2001 From: deve Date: Mon, 26 Sep 2016 11:28:19 +0200 Subject: [PATCH 231/350] Fixed compilation with cmake 3.6 --- lib/glew/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/glew/CMakeLists.txt b/lib/glew/CMakeLists.txt index 010773992..987e3e5b8 100644 --- a/lib/glew/CMakeLists.txt +++ b/lib/glew/CMakeLists.txt @@ -16,3 +16,5 @@ add_library(glew STATIC include/GL/glew.h include/GL/glxew.h include/GL/wglew.h src/glew.c src/glewinfo.c ) + +target_link_libraries(glew ${OPENGL_LIBRARIES}) From a58cef470162dd595a522a4142a447d6b0c98a8c Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 27 Sep 2016 08:42:05 +1000 Subject: [PATCH 232/350] Smoothing the speed of a kart (necessary to reduce camera stutter) results in difficult to reproduce physics. So maintain two speed values: smoothed (for camera), and 'normal' (for everything else). --- src/graphics/camera_normal.cpp | 8 ++++---- src/karts/abstract_kart.hpp | 4 ++++ src/karts/kart.cpp | 21 ++++++++++++--------- src/karts/kart.hpp | 7 +++++++ 4 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/graphics/camera_normal.cpp b/src/graphics/camera_normal.cpp index ea0bf098a..b2f43a0c6 100644 --- a/src/graphics/camera_normal.cpp +++ b/src/graphics/camera_normal.cpp @@ -81,7 +81,7 @@ void CameraNormal::smoothMoveCamera(float dt) const KartProperties *kp = m_kart->getKartProperties(); float max_increase_with_zipper = kp->getZipperMaxSpeedIncrease(); float max_speed_without_zipper = kp->getEngineMaxSpeed(); - float current_speed = m_kart->getSpeed(); + float current_speed = m_kart->getSmoothedSpeed(); const Skidding *ks = m_kart->getSkidding(); float skid_factor = ks->getVisualSkidRotation(); @@ -107,10 +107,10 @@ void CameraNormal::smoothMoveCamera(float dt) core::vector3df wanted_position = m_kart_camera_position_with_offset.toIrrVector(); float f = 5.0f; - if ((m_kart->getSpeed() > 5 ) || (m_kart->getSpeed() < 0 )) + if ((current_speed > 5 ) || (current_speed < 0 )) { - f = m_kart->getSpeed()>0 ? m_kart->getSpeed()/3 + 1.0f - : -1.5f * m_kart->getSpeed() + 2.0f; + f = current_speed >0 ? current_speed/3 + 1.0f + : -1.5f * current_speed + 2.0f; } current_position += (wanted_position - current_position) * (dt *f); diff --git a/src/karts/abstract_kart.hpp b/src/karts/abstract_kart.hpp index 7fba6d672..fff891401 100644 --- a/src/karts/abstract_kart.hpp +++ b/src/karts/abstract_kart.hpp @@ -274,6 +274,10 @@ public: * like Ghost. */ virtual float getSpeed() const = 0; // ------------------------------------------------------------------------ + /** Returns the exponentially smoothened speed of the kart in + * which is removes shaking from camera. */ + virtual float getSmoothedSpeed() const = 0; + // ------------------------------------------------------------------------ /** Returns the current maximum speed for this kart, this includes all * bonus and maluses that are currently applied. */ virtual float getCurrentMaxSpeed() const = 0; diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index b9edb0cd4..ee4f1e021 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -156,6 +156,7 @@ Kart::Kart (const std::string& ident, unsigned int world_kart_id, // Set position and heading: m_reset_transform = init_transform; m_speed = 0.0f; + m_smoothed_speed = 0.0f; m_kart_model->setKart(this); @@ -352,6 +353,7 @@ void Kart::reset() m_brake_time = 0.0f; m_time_last_crash = 0.0f; m_speed = 0.0f; + m_smoothed_speed = 0.0f; m_current_lean = 0.0f; m_view_blocked_by_plunger = 0.0f; m_bubblegum_time = 0.0f; @@ -1518,10 +1520,9 @@ void Kart::updateSpeed() // in the distance between kart and camera to jitter as well (typically // only in the order of centimetres though). Smoothing the speed value // gets rid of this jitter, and also r - float old_speed = m_speed; m_speed = getVehicle()->getRigidBody()->getLinearVelocity().length(); float f = 0.3f; - m_speed = f*m_speed + (1.0f - f)*old_speed; + m_smoothed_speed = f*m_speed + (1.0f - f)*m_smoothed_speed; // calculate direction of m_speed const btTransform& chassisTrans = getVehicle()->getChassisWorldTransform(); @@ -1531,16 +1532,18 @@ void Kart::updateSpeed() chassisTrans.getBasis()[2][2]); if (forwardW.dot(getVehicle()->getRigidBody()->getLinearVelocity()) < btScalar(0.)) - m_speed *= -1.f; + { + m_speed = -m_speed; + } // At low velocity, forces on kart push it back and forth so we ignore this - if (fabsf(m_speed) < 0.2f) // quick'n'dirty workaround for bug 1776883 - m_speed = 0; - - if (dynamic_cast(getKartAnimation()) || - dynamic_cast(getKartAnimation())) + // - quick'n'dirty workaround for bug 1776883 + if (fabsf(m_speed) < 0.2f || + dynamic_cast ( getKartAnimation() ) || + dynamic_cast( getKartAnimation() ) ) { - m_speed = 0; + m_speed = 0; + m_smoothed_speed = 0; } } // updateSpeed diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index be2bbb565..c4f858ad8 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -197,7 +197,11 @@ protected: /** When a kart has its view blocked by the plunger, this variable will be * > 0 the number it contains is the time left before removing plunger. */ float m_view_blocked_by_plunger; + /** The current speed (i.e. length of velocity vector) of this kart. */ float m_speed; + /** For camera handling an exponentially smoothened value is used, which + * reduces stuttering of the camera. */ + float m_smoothed_speed; std::vector m_custom_sounds; SFXBase *m_beep_sound; @@ -364,6 +368,9 @@ public: /** Returns the speed of the kart in meters/second. */ virtual float getSpeed() const {return m_speed; } // ------------------------------------------------------------------------ + /** Returns the speed of the kart in meters/second. */ + virtual float getSmoothedSpeed() const { return m_smoothed_speed; } + // ------------------------------------------------------------------------ /** This is used on the client side only to set the speed of the kart * from the server information. */ virtual void setSpeed(float s) {m_speed = s; } From 3030b679dffcaa71a58afc302bca1c08a910d4f7 Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 27 Sep 2016 12:38:36 +0800 Subject: [PATCH 233/350] Clean up ArenaAI --- src/karts/controller/arena_ai.cpp | 59 ++++++++++++++++++------------ src/karts/controller/arena_ai.hpp | 16 ++++---- src/karts/controller/battle_ai.cpp | 40 +++++++------------- src/karts/controller/battle_ai.hpp | 6 +-- src/karts/controller/soccer_ai.cpp | 18 +++++---- src/karts/controller/soccer_ai.hpp | 2 +- 6 files changed, 70 insertions(+), 71 deletions(-) diff --git a/src/karts/controller/arena_ai.cpp b/src/karts/controller/arena_ai.cpp index bc9bb9d4d..270ed0a52 100644 --- a/src/karts/controller/arena_ai.cpp +++ b/src/karts/controller/arena_ai.cpp @@ -51,12 +51,12 @@ void ArenaAI::reset() m_closest_kart = NULL; m_closest_kart_node = Graph::UNKNOWN_SECTOR; m_closest_kart_point = Vec3(0, 0, 0); - m_closest_kart_pos_data = {0}; - m_cur_kart_pos_data = {0}; m_is_stuck = false; m_is_uturn = false; m_avoiding_item = false; + m_mini_skid = false; m_target_point = Vec3(0, 0, 0); + m_target_point_lc = Vec3(0, 0, 0); m_time_since_last_shot = 0.0f; m_time_since_driving = 0.0f; m_time_since_reversing = 0.0f; @@ -102,6 +102,10 @@ void ArenaAI::update(float dt) findClosestKart(true); findTarget(); + + // After found target, convert it to local coordinate, used for skidding or + // u-turn + m_target_point_lc = m_kart->getTrans().inverse()(m_target_point); handleArenaItems(dt); if (m_kart->getSpeed() > 15.0f && m_turn_angle < 20) @@ -201,13 +205,13 @@ void ArenaAI::handleArenaSteering(const float dt) if (ignorePathFinding()) { // Steer directly - checkPosition(m_target_point, &m_cur_kart_pos_data); #ifdef AI_DEBUG m_debug_sphere->setPosition(m_target_point.toIrrVector()); #endif - if (m_cur_kart_pos_data.behind) + if (m_target_point_lc.z() < 0) { - m_adjusting_side = m_cur_kart_pos_data.lhs; + // Local coordinate z < 0 == target point is behind + m_adjusting_side = m_target_point_lc.x() < 0; m_is_uturn = true; } else @@ -224,16 +228,16 @@ void ArenaAI::handleArenaSteering(const float dt) updateTurnRadius(m_kart->getXYZ(), m_aiming_points[0], m_aiming_points[1]); m_target_point = m_aiming_points[1]; - checkPosition(m_target_point, &m_cur_kart_pos_data); + m_target_point_lc = m_kart->getTrans().inverse()(m_target_point); #ifdef AI_DEBUG m_debug_sphere->setVisible(true); m_debug_sphere_next->setVisible(true); m_debug_sphere->setPosition(m_aiming_points[0].toIrrVector()); m_debug_sphere_next->setPosition(m_aiming_points[1].toIrrVector()); #endif - if (m_cur_kart_pos_data.behind) + if (m_target_point_lc.z() < 0) { - m_adjusting_side = m_cur_kart_pos_data.lhs; + m_adjusting_side = m_target_point_lc.x() < 0; m_is_uturn = true; } else @@ -324,10 +328,10 @@ void ArenaAI::handleArenaUTurn(const float dt) setSteering(turn_side, dt); m_time_since_uturn += dt; - checkPosition(m_target_point, &m_cur_kart_pos_data); - if (!m_cur_kart_pos_data.behind || m_time_since_uturn > + if (m_target_point_lc.z() > 0 || m_time_since_uturn > (m_cur_difficulty == RaceManager::DIFFICULTY_EASY ? 3.5f : 3.0f)) { + // End U-turn until target point is in front of this AI m_is_uturn = false; m_time_since_uturn = 0.0f; } @@ -376,9 +380,8 @@ void ArenaAI::updateBadItemLocation() selected->getType() == Item::ITEM_BUBBLEGUM || selected->getType() == Item::ITEM_BUBBLEGUM_NOLOK)) { - Vec3 bad_item_lc; - checkPosition(selected->getXYZ(), NULL, &bad_item_lc, - true/*use_front_xyz*/); + Vec3 bad_item_lc = + m_kart->getTrans().inverse()(selected->getXYZ()); // If satisfy the below condition, AI should not be affected by it: // bad_item_lc.z() < 0.0f, behind the kart @@ -501,18 +504,28 @@ void ArenaAI::handleArenaItems(const float dt) // Find a closest kart again, this time we ignore difficulty findClosestKart(false); - if (!m_closest_kart) return; + Vec3 closest_kart_point_lc = + m_kart->getTrans().inverse()(m_closest_kart_point); + m_time_since_last_shot += dt; float min_bubble_time = 2.0f; const bool difficulty = m_cur_difficulty == RaceManager::DIFFICULTY_EASY || m_cur_difficulty == RaceManager::DIFFICULTY_MEDIUM; - const bool fire_behind = m_closest_kart_pos_data.behind && !difficulty; + const bool fire_behind = closest_kart_point_lc.z() < 0 && !difficulty; - const bool perfect_aim = m_closest_kart_pos_data.angle < 0.2f; + const float abs_angle = atan2f(fabsf(closest_kart_point_lc.x()), + fabsf(closest_kart_point_lc.z())); + const bool perfect_aim = abs_angle < 0.2f; + + // Compensate the distance because this distance is straight to straight + // in graph node, so if kart to kart are not facing like so as, their real + // distance maybe smaller + const float dist_to_kart = + getKartDistance(m_closest_kart->getWorldKartId()) * 0.8f; switch(m_kart->getPowerup()->getType()) { @@ -530,7 +543,7 @@ void ArenaAI::handleArenaItems(const float dt) if ((!m_kart->isShielded() && projectile_manager->projectileIsClose(m_kart, m_ai_properties->m_shield_incoming_radius)) || - (m_closest_kart_pos_data.distance < 15.0f && + (dist_to_kart < 15.0f && ((m_closest_kart->getAttachment()-> getType() == Attachment::ATTACH_SWATTER) || (m_closest_kart->getAttachment()-> @@ -544,11 +557,9 @@ void ArenaAI::handleArenaItems(const float dt) // Avoid dropping all bubble gums one after another if (m_time_since_last_shot < 3.0f) break; - // Use bubblegum if the next kart behind is 'close' but not too close, + // Use bubblegum if the kart around is close, // or can't find a close kart for too long time - if ((m_closest_kart_pos_data.distance < 15.0f && - m_closest_kart_pos_data.distance > 3.0f) || - m_time_since_last_shot > 15.0f) + if (dist_to_kart < 15.0f || m_time_since_last_shot > 15.0f) { m_controls->m_fire = true; m_controls->m_look_back = true; @@ -566,7 +577,7 @@ void ArenaAI::handleArenaItems(const float dt) // Leave some time between shots if (m_time_since_last_shot < 1.0f) break; - if (m_closest_kart_pos_data.distance < 25.0f && + if (dist_to_kart < 25.0f && !m_closest_kart->isInvulnerable()) { m_controls->m_fire = true; @@ -586,7 +597,7 @@ void ArenaAI::handleArenaItems(const float dt) // Leave some time between shots if (m_time_since_last_shot < 1.0f) break; - if (m_closest_kart_pos_data.distance < 6.0f && + if (dist_to_kart < 6.0f && (difficulty || perfect_aim) && !m_closest_kart->isInvulnerable()) { @@ -607,7 +618,7 @@ void ArenaAI::handleArenaItems(const float dt) break; if (!m_closest_kart->isSquashed() && - m_closest_kart_pos_data.distance < d2 && + dist_to_kart * dist_to_kart < d2 && m_closest_kart->getSpeed() < m_kart->getSpeed()) { m_controls->m_fire = true; diff --git a/src/karts/controller/arena_ai.hpp b/src/karts/controller/arena_ai.hpp index d0bb64819..dfb1b877f 100644 --- a/src/karts/controller/arena_ai.hpp +++ b/src/karts/controller/arena_ai.hpp @@ -22,7 +22,7 @@ #include "karts/controller/ai_base_controller.hpp" #include "race/race_manager.hpp" -#undef AI_DEBUG +#define AI_DEBUG #ifdef AI_DEBUG #include "graphics/irr_driver.hpp" #endif @@ -41,14 +41,14 @@ namespace irr class ArenaAI : public AIBaseController { protected: + ArenaGraph* m_graph; + /** Pointer to the closest kart around this kart. */ AbstractKart *m_closest_kart; int m_closest_kart_node; Vec3 m_closest_kart_point; - posData m_closest_kart_pos_data; - /** Holds the current difficulty. */ RaceManager::Difficulty m_cur_difficulty; @@ -57,7 +57,7 @@ protected: irr::scene::ISceneNode *m_debug_sphere; irr::scene::ISceneNode *m_debug_sphere_next; - /** The node(poly) at which the target point lies in. */ + /** The node(quad) at which the target point lies in. */ int m_target_node; /** The target point. */ @@ -68,13 +68,11 @@ protected: void collectItemInArena(Vec3*, int*) const; float findAngleFrom3Edges(float a, float b, float c); private: - ArenaGraph* m_graph; - /** Used by handleArenaUTurn, it tells whether to do left or right * turning when steering is overridden. */ bool m_adjusting_side; - posData m_cur_kart_pos_data; + Vec3 m_target_point_lc; /** Indicates that the kart is currently stuck, and m_time_since_reversing is * counting down. */ @@ -109,6 +107,8 @@ private: std::set m_aiming_nodes; std::vector m_aiming_points; + bool m_mini_skid; + void checkIfStuck(const float dt); void handleArenaAcceleration(const float dt); void handleArenaBraking(); @@ -125,8 +125,10 @@ private: virtual void resetAfterStop() {}; virtual void findClosestKart(bool use_difficulty) = 0; virtual void findTarget() = 0; + virtual float getKartDistance(int to_id) const = 0; virtual bool forceBraking() { return m_avoiding_item; } virtual bool ignorePathFinding() { return false; } + virtual bool canSkid(float steer_fraction) { return m_mini_skid; } public: ArenaAI(AbstractKart *kart); virtual ~ArenaAI() {}; diff --git a/src/karts/controller/battle_ai.cpp b/src/karts/controller/battle_ai.cpp index 3fab6f7dd..71ef2084e 100644 --- a/src/karts/controller/battle_ai.cpp +++ b/src/karts/controller/battle_ai.cpp @@ -25,12 +25,10 @@ #include "karts/abstract_kart.hpp" #include "karts/controller/kart_control.hpp" #include "modes/three_strikes_battle.hpp" +#include "tracks/arena_graph.hpp" #ifdef AI_DEBUG #include "irrlicht.h" -#include -using namespace irr; -using namespace std; #endif BattleAI::BattleAI(AbstractKart *kart) @@ -72,14 +70,11 @@ BattleAI::~BattleAI() void BattleAI::reset() { ArenaAI::reset(); - AIBaseController::reset(); - m_mini_skid = false; } // reset //----------------------------------------------------------------------------- void BattleAI::update(float dt) { - m_mini_skid = false; ArenaAI::update(dt); } // update @@ -118,33 +113,19 @@ void BattleAI::findClosestKart(bool use_difficulty) continue; } - Vec3 d = kart->getXYZ() - m_kart->getXYZ(); - if (d.length() <= distance) + float dist_to_kart = m_graph->getDistance(getCurrentNode(), + m_world->getKartNode(kart->getWorldKartId())); + if (dist_to_kart <= distance) { - distance = d.length(); + distance = dist_to_kart; closest_kart_num = i; } } - const AbstractKart* closest_kart = m_world->getKart(closest_kart_num); + m_closest_kart = m_world->getKart(closest_kart_num); m_closest_kart_node = m_world->getKartNode(closest_kart_num); - m_closest_kart_point = closest_kart->getXYZ(); + m_closest_kart_point = m_closest_kart->getXYZ(); - if (!use_difficulty) - { - m_closest_kart = m_world->getKart(closest_kart_num); - checkPosition(m_closest_kart_point, &m_closest_kart_pos_data); - - // Do a mini-skid to closest kart only when firing target, - // not straight ahead, not too far, in front of it - // and with suitable difficulties. - if (m_closest_kart_pos_data.angle > 0.2f && - m_closest_kart_pos_data.distance < 20.0f && - !m_closest_kart_pos_data.behind && - (m_cur_difficulty == RaceManager::DIFFICULTY_HARD || - m_cur_difficulty == RaceManager::DIFFICULTY_BEST)) - m_mini_skid = true; - } } // findClosestKart //----------------------------------------------------------------------------- @@ -166,8 +147,15 @@ int BattleAI::getCurrentNode() const { return m_world->getKartNode(m_kart->getWorldKartId()); } // getCurrentNode + //----------------------------------------------------------------------------- bool BattleAI::isWaiting() const { return m_world->isStartPhase(); } // isWaiting + +//----------------------------------------------------------------------------- +float BattleAI::getKartDistance(int to_id) const +{ + return m_graph->getDistance(getCurrentNode(), m_world->getKartNode(to_id)); +} // getKartDistance diff --git a/src/karts/controller/battle_ai.hpp b/src/karts/controller/battle_ai.hpp index 4899ee090..5677bcad4 100644 --- a/src/karts/controller/battle_ai.hpp +++ b/src/karts/controller/battle_ai.hpp @@ -24,8 +24,6 @@ #include "karts/controller/arena_ai.hpp" class ThreeStrikesBattle; -class Vec3; -class Item; /** The actual battle AI. * \ingroup controller @@ -36,13 +34,11 @@ private: /** Keep a pointer to world. */ ThreeStrikesBattle *m_world; - bool m_mini_skid; - virtual void findClosestKart(bool use_difficulty); virtual void findTarget(); virtual int getCurrentNode() const; virtual bool isWaiting() const; - virtual bool canSkid(float steer_fraction) { return m_mini_skid; } + virtual float getKartDistance(int to_id) const; public: BattleAI(AbstractKart *kart); ~BattleAI(); diff --git a/src/karts/controller/soccer_ai.cpp b/src/karts/controller/soccer_ai.cpp index 146fdffd0..d42dd5b22 100644 --- a/src/karts/controller/soccer_ai.cpp +++ b/src/karts/controller/soccer_ai.cpp @@ -24,12 +24,10 @@ #include "karts/controller/kart_control.hpp" #include "karts/kart_properties.hpp" #include "modes/soccer_world.hpp" +#include "tracks/arena_graph.hpp" #ifdef AI_DEBUG #include "irrlicht.h" -#include -using namespace irr; -using namespace std; #endif #ifdef BALL_AIM_DEBUG @@ -94,7 +92,6 @@ SoccerAI::~SoccerAI() void SoccerAI::reset() { ArenaAI::reset(); - AIBaseController::reset(); m_overtake_ball = false; m_force_brake = false; @@ -154,11 +151,9 @@ void SoccerAI::findClosestKart(bool use_difficulty) } } - const AbstractKart* closest_kart = m_world->getKart(closest_kart_num); - m_closest_kart_node = m_world->getKartNode(closest_kart_num); - m_closest_kart_point = closest_kart->getXYZ(); m_closest_kart = m_world->getKart(closest_kart_num); - checkPosition(m_closest_kart_point, &m_closest_kart_pos_data); + m_closest_kart_node = m_world->getKartNode(closest_kart_num); + m_closest_kart_point = m_closest_kart->getXYZ(); } // findClosestKart @@ -457,8 +452,15 @@ int SoccerAI::getCurrentNode() const { return m_world->getKartNode(m_kart->getWorldKartId()); } // getCurrentNode + //----------------------------------------------------------------------------- bool SoccerAI::isWaiting() const { return m_world->isStartPhase(); } // isWaiting + +//----------------------------------------------------------------------------- +float SoccerAI::getKartDistance(int to_id) const +{ + return m_graph->getDistance(getCurrentNode(), m_world->getKartNode(to_id)); +} // getKartDistance diff --git a/src/karts/controller/soccer_ai.hpp b/src/karts/controller/soccer_ai.hpp index edfd52f8d..974ec55b5 100644 --- a/src/karts/controller/soccer_ai.hpp +++ b/src/karts/controller/soccer_ai.hpp @@ -27,7 +27,6 @@ #endif class SoccerWorld; -class Vec3; /** The actual soccer AI. * \ingroup controller @@ -64,6 +63,7 @@ private: virtual void findTarget(); virtual void resetAfterStop() OVERRIDE { m_overtake_ball = false; } virtual int getCurrentNode() const; + virtual float getKartDistance(int to_id) const; virtual bool isWaiting() const; virtual bool canSkid(float steer_fraction) { return false; } virtual bool forceBraking() OVERRIDE From e26920e479a33bf4ba252f96997bb2410241243a Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 27 Sep 2016 15:30:31 +0800 Subject: [PATCH 234/350] Improved skidding when closing to item or kart as target --- src/karts/controller/arena_ai.cpp | 35 +++++++++++++++++++++++++----- src/karts/controller/arena_ai.hpp | 10 +++++---- src/karts/controller/soccer_ai.hpp | 3 ++- src/modes/soccer_world.cpp | 2 -- 4 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/karts/controller/arena_ai.cpp b/src/karts/controller/arena_ai.cpp index 270ed0a52..6240120bb 100644 --- a/src/karts/controller/arena_ai.cpp +++ b/src/karts/controller/arena_ai.cpp @@ -23,7 +23,6 @@ #include "items/powerup.hpp" #include "items/projectile_manager.hpp" #include "karts/abstract_kart.hpp" -#include "karts/controller/player_controller.hpp" #include "karts/controller/ai_properties.hpp" #include "karts/kart_properties.hpp" #include "tracks/arena_graph.hpp" @@ -63,6 +62,7 @@ void ArenaAI::reset() m_time_since_uturn = 0.0f; m_turn_radius = 0.0f; m_turn_angle = 0.0f; + m_steering_angle = 0.0f; m_on_node.clear(); m_aiming_points.clear(); m_aiming_nodes.clear(); @@ -106,6 +106,7 @@ void ArenaAI::update(float dt) // After found target, convert it to local coordinate, used for skidding or // u-turn m_target_point_lc = m_kart->getTrans().inverse()(m_target_point); + doSkiddingTest(); handleArenaItems(dt); if (m_kart->getSpeed() > 15.0f && m_turn_angle < 20) @@ -216,8 +217,8 @@ void ArenaAI::handleArenaSteering(const float dt) } else { - float target_angle = steerToPoint(m_target_point); - setSteering(target_angle, dt); + m_steering_angle = steerToPoint(m_target_point); + setSteering(m_steering_angle, dt); } return; } @@ -242,8 +243,8 @@ void ArenaAI::handleArenaSteering(const float dt) } else { - float target_angle = steerToPoint(m_target_point); - setSteering(target_angle, dt); + m_steering_angle = steerToPoint(m_target_point); + setSteering(m_steering_angle, dt); } return; } @@ -713,3 +714,27 @@ void ArenaAI::collectItemInArena(Vec3* aim_point, int* target_node) const *target_node = m_closest_kart_node; } } // collectItemInArena + +//----------------------------------------------------------------------------- +void ArenaAI::doSkiddingTest() +{ + m_mini_skid = false; + + // No skidding when u-turn + if (m_is_uturn) return; + + // Skid when close to target, but not straight ahead, in front of it, same + // steering side and with suitable difficulties. + const float abs_angle = atan2f(fabsf(m_target_point_lc.x()), + fabsf(m_target_point_lc.z())); + if ((m_cur_difficulty == RaceManager::DIFFICULTY_HARD || + m_cur_difficulty == RaceManager::DIFFICULTY_BEST) && + m_target_point_lc.z() > 0 && abs_angle > 0.15f && + m_target_point_lc.length() < 10.0f && + ((m_steering_angle < 0 && m_target_point_lc.x() < 0) || + (m_steering_angle > 0 && m_target_point_lc.x() > 0))) + { + m_mini_skid = true; + } + +} // doSkiddingTest diff --git a/src/karts/controller/arena_ai.hpp b/src/karts/controller/arena_ai.hpp index dfb1b877f..d5540ba55 100644 --- a/src/karts/controller/arena_ai.hpp +++ b/src/karts/controller/arena_ai.hpp @@ -22,7 +22,7 @@ #include "karts/controller/ai_base_controller.hpp" #include "race/race_manager.hpp" -#define AI_DEBUG +#undef AI_DEBUG #ifdef AI_DEBUG #include "graphics/irr_driver.hpp" #endif @@ -65,8 +65,9 @@ protected: bool m_avoiding_item; + bool m_mini_skid; + void collectItemInArena(Vec3*, int*) const; - float findAngleFrom3Edges(float a, float b, float c); private: /** Used by handleArenaUTurn, it tells whether to do left or right * turning when steering is overridden. */ @@ -100,6 +101,7 @@ private: float m_turn_radius; float m_turn_angle; + float m_steering_angle; Vec3 m_current_forward_point; int m_current_forward_node; @@ -107,9 +109,9 @@ private: std::set m_aiming_nodes; std::vector m_aiming_points; - bool m_mini_skid; - void checkIfStuck(const float dt); + void doSkiddingTest(); + float findAngleFrom3Edges(float a, float b, float c); void handleArenaAcceleration(const float dt); void handleArenaBraking(); void handleArenaItems(const float dt); diff --git a/src/karts/controller/soccer_ai.hpp b/src/karts/controller/soccer_ai.hpp index 974ec55b5..618592607 100644 --- a/src/karts/controller/soccer_ai.hpp +++ b/src/karts/controller/soccer_ai.hpp @@ -65,7 +65,8 @@ private: virtual int getCurrentNode() const; virtual float getKartDistance(int to_id) const; virtual bool isWaiting() const; - virtual bool canSkid(float steer_fraction) { return false; } + virtual bool canSkid(float steer_fraction) + { return m_mini_skid && !(m_overtake_ball || m_chasing_ball); } virtual bool forceBraking() OVERRIDE { return m_avoiding_item || m_force_brake; } virtual bool ignorePathFinding() OVERRIDE diff --git a/src/modes/soccer_world.cpp b/src/modes/soccer_world.cpp index eba9e1479..564a22ef6 100644 --- a/src/modes/soccer_world.cpp +++ b/src/modes/soccer_world.cpp @@ -25,13 +25,11 @@ #include "graphics/central_settings.hpp" #include "graphics/irr_driver.hpp" #include "graphics/render_info.hpp" -#include "karts/abstract_kart.hpp" #include "karts/kart.hpp" #include "karts/kart_model.hpp" #include "karts/kart_properties.hpp" #include "karts/rescue_animation.hpp" #include "karts/controller/local_player_controller.hpp" -#include "karts/controller/soccer_ai.hpp" #include "physics/physics.hpp" #include "states_screens/race_gui_base.hpp" #include "tracks/track.hpp" From ff52d9137256120dd598236aa73f5fdbff043fee Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 27 Sep 2016 16:24:38 +0800 Subject: [PATCH 235/350] Improve SoccerAi front point handling --- src/karts/controller/ai_base_controller.cpp | 32 --------------------- src/karts/controller/ai_base_controller.hpp | 6 ---- src/karts/controller/soccer_ai.cpp | 13 +++++---- src/karts/controller/soccer_ai.hpp | 4 +++ src/karts/kart.cpp | 6 ++-- 5 files changed, 15 insertions(+), 46 deletions(-) diff --git a/src/karts/controller/ai_base_controller.cpp b/src/karts/controller/ai_base_controller.cpp index b44c91453..0db7cce16 100644 --- a/src/karts/controller/ai_base_controller.cpp +++ b/src/karts/controller/ai_base_controller.cpp @@ -279,35 +279,3 @@ void AIBaseController::crashed(const Material *m) } } // crashed(Material) - -//----------------------------------------------------------------------------- -void AIBaseController::checkPosition(const Vec3 &point, posData *pos_data, - Vec3 *lc, bool use_front_xyz) const -{ - // Convert to local coordinates from the point of view of current kart - btTransform t; - t.setBasis(m_kart->getTrans().getBasis()); - t.setOrigin(use_front_xyz ? m_kart->getFrontXYZ() : m_kart->getXYZ()); - Vec3 local_coordinates = t.inverse()(point); - - // Save local coordinates for later use if needed - if (lc) *lc = local_coordinates; - - if (pos_data == NULL) return; - // lhs: tell whether it's left or right hand side - if (local_coordinates.getX() < 0) - pos_data->lhs = true; - else - pos_data->lhs = false; - - // behind: tell whether it's behind or not - if (local_coordinates.getZ() < 0) - pos_data->behind = true; - else - pos_data->behind = false; - - pos_data->angle = atan2f(fabsf(local_coordinates.getX()), - fabsf(local_coordinates.getZ())); - pos_data->distance = local_coordinates.length(); - -} // checkPosition diff --git a/src/karts/controller/ai_base_controller.hpp b/src/karts/controller/ai_base_controller.hpp index d52c70672..fa25cb670 100644 --- a/src/karts/controller/ai_base_controller.hpp +++ b/src/karts/controller/ai_base_controller.hpp @@ -64,9 +64,6 @@ protected: * for AI testing only. */ static int m_test_ai; - /** Position info structure of targets. */ - struct posData {bool behind; bool lhs; float angle; float distance;}; - void setControllerName(const std::string &name); float steerToPoint(const Vec3 &point); float normalizeAngle(float angle); @@ -77,9 +74,6 @@ protected: /** This can be called to detect if the kart is stuck (i.e. repeatedly * hitting part of the track). */ bool isStuck() const { return m_stuck; } - void checkPosition(const Vec3&, posData*, - Vec3* lc = NULL, - bool use_front_xyz = false) const; public: AIBaseController(AbstractKart *kart); diff --git a/src/karts/controller/soccer_ai.cpp b/src/karts/controller/soccer_ai.cpp index d42dd5b22..1ac9d47a4 100644 --- a/src/karts/controller/soccer_ai.cpp +++ b/src/karts/controller/soccer_ai.cpp @@ -97,6 +97,9 @@ void SoccerAI::reset() m_force_brake = false; m_chasing_ball = false; + m_front_transform.setOrigin(m_kart->getFrontXYZ()); + m_front_transform.setBasis(m_kart->getTrans().getBasis()); + } // reset //----------------------------------------------------------------------------- @@ -110,6 +113,8 @@ void SoccerAI::update(float dt) #endif m_force_brake = false; m_chasing_ball = false; + m_front_transform.setOrigin(m_kart->getFrontXYZ()); + m_front_transform.setBasis(m_kart->getTrans().getBasis()); if (m_world->getPhase() == World::GOAL_PHASE) { @@ -209,10 +214,8 @@ Vec3 SoccerAI::determineBallAimingPosition() const Vec3& ball_aim_pos = m_world->getBallAimPosition(m_opp_team); const Vec3& orig_pos = m_world->getBallPosition(); - Vec3 ball_lc; - Vec3 aim_lc; - checkPosition(orig_pos, NULL, &ball_lc, true/*use_front_xyz*/); - checkPosition(ball_aim_pos, NULL, &aim_lc, true/*use_front_xyz*/); + Vec3 ball_lc = m_front_transform.inverse()(orig_pos); + Vec3 aim_lc = m_front_transform.inverse()(ball_aim_pos); // Too far from the ball, // use path finding from arena ai to get close @@ -230,7 +233,7 @@ Vec3 SoccerAI::determineBallAimingPosition() return ball_aim_pos; } else - return m_kart->getTrans()(Vec3(overtake_lc)); + return m_front_transform(overtake_lc); } else { diff --git a/src/karts/controller/soccer_ai.hpp b/src/karts/controller/soccer_ai.hpp index 618592607..b5a029d22 100644 --- a/src/karts/controller/soccer_ai.hpp +++ b/src/karts/controller/soccer_ai.hpp @@ -21,6 +21,8 @@ #include "karts/controller/arena_ai.hpp" +#include "LinearMath/btTransform.h" + #undef BALL_AIM_DEBUG #ifdef BALL_AIM_DEBUG #include "graphics/irr_driver.hpp" @@ -53,6 +55,8 @@ private: bool m_force_brake; bool m_chasing_ball; + btTransform m_front_transform; + Vec3 determineBallAimingPosition(); bool isOvertakable(const Vec3& ball_lc); bool determineOvertakePosition(const Vec3& ball_lc, const Vec3& aim_lc, diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index d0c52bf9b..d3ab47c42 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1210,6 +1210,9 @@ void Kart::update(float dt) // Update the position and other data taken from the physics Moveable::update(dt); + Vec3 front(0, 0, getKartLength()*0.5f); + m_xyz_front = getTrans()(front); + if(!history->replayHistory()) m_controller->update(dt); @@ -1333,9 +1336,6 @@ void Kart::update(float dt) m_body->getBroadphaseHandle()->m_collisionFilterGroup = 0; } - Vec3 front(0, 0, getKartLength()*0.5f); - m_xyz_front = getTrans()(front); - // After the physics step was done, the position of the wheels (as stored // in wheelInfo) is actually outdated, since the chassis was moved // according to the force acting from the wheels. So the center of the From 7867a3027621f3fc5a910e531f4eef1493e66b23 Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 28 Sep 2016 09:59:33 +0800 Subject: [PATCH 236/350] Allow auto-rescue for ArenaAI if off road for some times --- src/karts/controller/arena_ai.cpp | 21 +++++++++++++++++++- src/karts/controller/arena_ai.hpp | 27 +++++++++++++++----------- src/karts/controller/battle_ai.cpp | 6 ++++++ src/karts/controller/battle_ai.hpp | 15 ++++++++------- src/karts/controller/soccer_ai.cpp | 6 ++++++ src/karts/controller/soccer_ai.hpp | 31 +++++++++++++++--------------- src/modes/soccer_world.cpp | 7 +++++++ src/modes/soccer_world.hpp | 2 ++ src/modes/three_strikes_battle.cpp | 7 +++++++ src/modes/three_strikes_battle.hpp | 2 +- 10 files changed, 89 insertions(+), 35 deletions(-) diff --git a/src/karts/controller/arena_ai.cpp b/src/karts/controller/arena_ai.cpp index 6240120bb..4055f7a6d 100644 --- a/src/karts/controller/arena_ai.cpp +++ b/src/karts/controller/arena_ai.cpp @@ -25,9 +25,9 @@ #include "karts/abstract_kart.hpp" #include "karts/controller/ai_properties.hpp" #include "karts/kart_properties.hpp" +#include "karts/rescue_animation.hpp" #include "tracks/arena_graph.hpp" #include "tracks/arena_node.hpp" -#include "utils/log.hpp" ArenaAI::ArenaAI(AbstractKart *kart) : AIBaseController(kart) @@ -58,6 +58,7 @@ void ArenaAI::reset() m_target_point_lc = Vec3(0, 0, 0); m_time_since_last_shot = 0.0f; m_time_since_driving = 0.0f; + m_time_since_off_road = 0.0f; m_time_since_reversing = 0.0f; m_time_since_uturn = 0.0f; m_turn_radius = 0.0f; @@ -90,6 +91,24 @@ void ArenaAI::update(float dt) return; } + if (!isKartOnRoad() && !m_kart->getKartAnimation()) + { + m_time_since_off_road += dt; + } + else if (m_time_since_off_road != 0.0f) + { + m_time_since_off_road = 0.0f; + } + + // If the kart needs to be rescued, do it now (and nothing else) + if (m_time_since_off_road > 5.0f && !m_kart->getKartAnimation()) + { + m_time_since_off_road = 0.0f; + new RescueAnimation(m_kart); + AIBaseController::update(dt); + return; + } + if (isWaiting()) { AIBaseController::update(dt); diff --git a/src/karts/controller/arena_ai.hpp b/src/karts/controller/arena_ai.hpp index d5540ba55..d314458c6 100644 --- a/src/karts/controller/arena_ai.hpp +++ b/src/karts/controller/arena_ai.hpp @@ -99,6 +99,9 @@ private: /** This is a timer that counts down when the kart is doing u-turn. */ float m_time_since_uturn; + /** This is a timer that counts when the kart start going off road. */ + float m_time_since_off_road; + float m_turn_radius; float m_turn_angle; float m_steering_angle; @@ -122,21 +125,23 @@ private: void updateBadItemLocation(); void updateTurnRadius(const Vec3& p1, const Vec3& p2, const Vec3& p3); - virtual int getCurrentNode() const = 0; - virtual bool isWaiting() const = 0; - virtual void resetAfterStop() {}; - virtual void findClosestKart(bool use_difficulty) = 0; - virtual void findTarget() = 0; + virtual bool canSkid(float steer_fraction) OVERRIDE + { return m_mini_skid; } + virtual void findClosestKart(bool use_difficulty) = 0; + virtual void findTarget() = 0; + virtual bool forceBraking() { return m_avoiding_item; } + virtual int getCurrentNode() const = 0; virtual float getKartDistance(int to_id) const = 0; - virtual bool forceBraking() { return m_avoiding_item; } - virtual bool ignorePathFinding() { return false; } - virtual bool canSkid(float steer_fraction) { return m_mini_skid; } + virtual bool ignorePathFinding() { return false; } + virtual bool isWaiting() const = 0; + virtual bool isKartOnRoad() const = 0; + virtual void resetAfterStop() {}; public: ArenaAI(AbstractKart *kart); virtual ~ArenaAI() {}; - virtual void update (float delta); - virtual void reset (); - virtual void newLap(int lap) {}; + virtual void update (float delta) OVERRIDE; + virtual void reset () OVERRIDE; + virtual void newLap (int lap) OVERRIDE {} }; #endif diff --git a/src/karts/controller/battle_ai.cpp b/src/karts/controller/battle_ai.cpp index 71ef2084e..b960f17b7 100644 --- a/src/karts/controller/battle_ai.cpp +++ b/src/karts/controller/battle_ai.cpp @@ -159,3 +159,9 @@ float BattleAI::getKartDistance(int to_id) const { return m_graph->getDistance(getCurrentNode(), m_world->getKartNode(to_id)); } // getKartDistance + +//----------------------------------------------------------------------------- +bool BattleAI::isKartOnRoad() const +{ + return m_world->isOnRoad(m_kart->getWorldKartId()); +} // isKartOnRoad diff --git a/src/karts/controller/battle_ai.hpp b/src/karts/controller/battle_ai.hpp index 5677bcad4..bb71ad65e 100644 --- a/src/karts/controller/battle_ai.hpp +++ b/src/karts/controller/battle_ai.hpp @@ -34,16 +34,17 @@ private: /** Keep a pointer to world. */ ThreeStrikesBattle *m_world; - virtual void findClosestKart(bool use_difficulty); - virtual void findTarget(); - virtual int getCurrentNode() const; - virtual bool isWaiting() const; - virtual float getKartDistance(int to_id) const; + virtual void findClosestKart(bool use_difficulty) OVERRIDE; + virtual void findTarget() OVERRIDE; + virtual int getCurrentNode() const OVERRIDE; + virtual float getKartDistance(int to_id) const OVERRIDE; + virtual bool isKartOnRoad() const OVERRIDE; + virtual bool isWaiting() const OVERRIDE; public: BattleAI(AbstractKart *kart); ~BattleAI(); - virtual void update (float delta); - virtual void reset (); + virtual void update (float delta) OVERRIDE; + virtual void reset () OVERRIDE; }; #endif diff --git a/src/karts/controller/soccer_ai.cpp b/src/karts/controller/soccer_ai.cpp index 1ac9d47a4..150b7dd19 100644 --- a/src/karts/controller/soccer_ai.cpp +++ b/src/karts/controller/soccer_ai.cpp @@ -467,3 +467,9 @@ float SoccerAI::getKartDistance(int to_id) const { return m_graph->getDistance(getCurrentNode(), m_world->getKartNode(to_id)); } // getKartDistance + +//----------------------------------------------------------------------------- +bool SoccerAI::isKartOnRoad() const +{ + return m_world->isOnRoad(m_kart->getWorldKartId()); +} // isKartOnRoad diff --git a/src/karts/controller/soccer_ai.hpp b/src/karts/controller/soccer_ai.hpp index b5a029d22..fe6e00ae6 100644 --- a/src/karts/controller/soccer_ai.hpp +++ b/src/karts/controller/soccer_ai.hpp @@ -57,29 +57,30 @@ private: btTransform m_front_transform; - Vec3 determineBallAimingPosition(); - bool isOvertakable(const Vec3& ball_lc); - bool determineOvertakePosition(const Vec3& ball_lc, const Vec3& aim_lc, - Vec3* overtake_lc); + Vec3 determineBallAimingPosition(); + bool determineOvertakePosition(const Vec3& ball_lc, const Vec3& aim_lc, + Vec3* overtake_lc); + bool isOvertakable(const Vec3& ball_lc); float rotateSlope(float old_slope, bool rotate_up); - virtual void findClosestKart(bool use_difficulty); - virtual void findTarget(); - virtual void resetAfterStop() OVERRIDE { m_overtake_ball = false; } - virtual int getCurrentNode() const; - virtual float getKartDistance(int to_id) const; - virtual bool isWaiting() const; - virtual bool canSkid(float steer_fraction) + virtual bool canSkid(float steer_fraction) OVERRIDE { return m_mini_skid && !(m_overtake_ball || m_chasing_ball); } - virtual bool forceBraking() OVERRIDE + virtual void findClosestKart(bool use_difficulty) OVERRIDE; + virtual void findTarget() OVERRIDE; + virtual bool forceBraking() OVERRIDE { return m_avoiding_item || m_force_brake; } - virtual bool ignorePathFinding() OVERRIDE + virtual int getCurrentNode() const OVERRIDE; + virtual float getKartDistance(int to_id) const OVERRIDE; + virtual bool ignorePathFinding() OVERRIDE { return m_overtake_ball || m_chasing_ball; } + virtual bool isKartOnRoad() const OVERRIDE; + virtual bool isWaiting() const OVERRIDE; + virtual void resetAfterStop() OVERRIDE { m_overtake_ball = false; } public: SoccerAI(AbstractKart *kart); ~SoccerAI(); - virtual void update (float delta); - virtual void reset (); + virtual void update (float delta) OVERRIDE; + virtual void reset () OVERRIDE; }; #endif diff --git a/src/modes/soccer_world.cpp b/src/modes/soccer_world.cpp index 564a22ef6..96f4c16d6 100644 --- a/src/modes/soccer_world.cpp +++ b/src/modes/soccer_world.cpp @@ -756,3 +756,10 @@ void SoccerWorld::setAITeam() Log::debug("SoccerWorld","blue AI: %d red AI: %d", m_blue_ai, m_red_ai); } // setAITeam + +//----------------------------------------------------------------------------- +bool SoccerWorld::isOnRoad(unsigned int kart_id) const +{ + assert(m_kart_track_sector.size() > kart_id); + return m_kart_track_sector[kart_id]->isOnRoad(); +} // isOnRoad diff --git a/src/modes/soccer_world.hpp b/src/modes/soccer_world.hpp index 97d57b2e5..8cb9c4c33 100644 --- a/src/modes/soccer_world.hpp +++ b/src/modes/soccer_world.hpp @@ -358,6 +358,8 @@ public: // ------------------------------------------------------------------------ int getKartNode(unsigned int kart_id) const; // ------------------------------------------------------------------------ + bool isOnRoad(unsigned int kart_id) const; + // ------------------------------------------------------------------------ int getBallNode() const; // ------------------------------------------------------------------------ const Vec3& getBallPosition() const diff --git a/src/modes/three_strikes_battle.cpp b/src/modes/three_strikes_battle.cpp index 535671d1c..d717b7201 100644 --- a/src/modes/three_strikes_battle.cpp +++ b/src/modes/three_strikes_battle.cpp @@ -565,3 +565,10 @@ void ThreeStrikesBattle::enterRaceOverState() } } // enterRaceOverState + +//----------------------------------------------------------------------------- +bool ThreeStrikesBattle::isOnRoad(unsigned int kart_id) const +{ + assert(m_kart_track_sector.size() > kart_id); + return m_kart_track_sector[kart_id]->isOnRoad(); +} // isOnRoad diff --git a/src/modes/three_strikes_battle.hpp b/src/modes/three_strikes_battle.hpp index 07f90ad27..bd9dbe194 100644 --- a/src/modes/three_strikes_battle.hpp +++ b/src/modes/three_strikes_battle.hpp @@ -119,7 +119,7 @@ public: virtual void enterRaceOverState() OVERRIDE; int getKartNode(unsigned int kart_id) const; - + bool isOnRoad(unsigned int kart_id) const; void updateKartRanks(); void increaseRescueCount() { m_total_rescue++; } }; // ThreeStrikesBattles From 3d89d5adc65c8231f883783d2776e1df6bf3ac2a Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 28 Sep 2016 11:24:04 +0800 Subject: [PATCH 237/350] Make OverWorld be derived from World --- src/modes/overworld.cpp | 8 ++++---- src/modes/overworld.hpp | 9 ++++----- src/modes/three_strikes_battle.hpp | 22 +++++++++++----------- src/modes/world.cpp | 26 +++++++++++++++++++++----- src/modes/world.hpp | 4 ++-- src/modes/world_with_rank.cpp | 20 -------------------- src/modes/world_with_rank.hpp | 8 +------- 7 files changed, 43 insertions(+), 54 deletions(-) diff --git a/src/modes/overworld.cpp b/src/modes/overworld.cpp index 582f15f50..7bc034b0a 100644 --- a/src/modes/overworld.cpp +++ b/src/modes/overworld.cpp @@ -39,7 +39,7 @@ #include "tracks/track_object_manager.hpp" //----------------------------------------------------------------------------- -OverWorld::OverWorld() : WorldWithRank() +OverWorld::OverWorld() : World() { m_return_to_garage = false; m_stop_music_when_dialog_open = false; @@ -118,8 +118,8 @@ void OverWorld::update(float dt) music_manager->startMusic(); m_karts[0]->startEngineSFX(); } - WorldWithRank::update(dt); - WorldWithRank::updateTrack(dt); + World::update(dt); + World::updateTrack(dt); const unsigned int kart_amount = (unsigned int)m_karts.size(); // isn't it cool, on the overworld nitro is free! @@ -269,7 +269,7 @@ void OverWorld::onMouseClick(int x, int y) if(challenge) { // Use the 'get closest start point' rescue function - // from WorldWithRank by setting the kart's position to + // from World by setting the kart's position to // be the location of the challenge bubble. AbstractKart* kart = getKart(0); kart->setXYZ(challenge->m_position); diff --git a/src/modes/overworld.hpp b/src/modes/overworld.hpp index a0bb6a0b3..1835411fd 100644 --- a/src/modes/overworld.hpp +++ b/src/modes/overworld.hpp @@ -20,19 +20,18 @@ #include -#include "modes/world_with_rank.hpp" +#include "modes/world.hpp" #include "utils/aligned_array.hpp" #include "LinearMath/btTransform.h" /* * The overworld map where challenges are played. - * \note This mode derives from LinearWorld to get support for drivelines, - * minimap and rescue, even though this world is not technically - * linear. + * \note Extends world to make a simple world where karts can drive around, + * it adds challenges and starting of races. * \ingroup modes */ -class OverWorld : public WorldWithRank +class OverWorld : public World { protected: diff --git a/src/modes/three_strikes_battle.hpp b/src/modes/three_strikes_battle.hpp index bd9dbe194..24a6dee10 100644 --- a/src/modes/three_strikes_battle.hpp +++ b/src/modes/three_strikes_battle.hpp @@ -95,27 +95,27 @@ public: ThreeStrikesBattle(); virtual ~ThreeStrikesBattle(); - virtual void init(); + virtual void init() OVERRIDE; // clock events - virtual bool isRaceOver(); - virtual void terminateRace(); + virtual bool isRaceOver() OVERRIDE; + virtual void terminateRace() OVERRIDE; // overriding World methods - virtual void reset(); + virtual void reset() OVERRIDE; //virtual void getDefaultCollectibles(int& collectible_type, int& amount); - virtual bool useFastMusicNearEnd() const { return false; } + virtual bool useFastMusicNearEnd() const OVERRIDE { return false; } virtual void getKartsDisplayInfo( - std::vector *info); - virtual bool raceHasLaps(){ return false; } + std::vector *info) OVERRIDE; + virtual bool raceHasLaps() OVERRIDE { return false; } - virtual const std::string& getIdent() const; + virtual const std::string& getIdent() const OVERRIDE; - virtual void kartHit(const unsigned int kart_id); - virtual void update(float dt); + virtual void kartHit(const unsigned int kart_id) OVERRIDE; + virtual void update(float dt) OVERRIDE; - virtual void kartAdded(AbstractKart* kart, scene::ISceneNode* node); + virtual void kartAdded(AbstractKart* kart, scene::ISceneNode* node) OVERRIDE; virtual void enterRaceOverState() OVERRIDE; int getKartNode(unsigned int kart_id) const; diff --git a/src/modes/world.cpp b/src/modes/world.cpp index c5d8a1242..1d269305e 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -1237,20 +1237,36 @@ void World::unpause() void World::delayedSelfDestruct() { m_self_destruct = true; -} +} // delayedSelfDestruct //----------------------------------------------------------------------------- - void World::escapePressed() { new RacePausedDialog(0.8f, 0.6f); -} +} // escapePressed //----------------------------------------------------------------------------- - bool World::isFogEnabled() const { return !m_force_disable_fog && (m_track != NULL && m_track->isFogEnabled()); -} +} // isFogEnabled + +// ---------------------------------------------------------------------------- +/** Returns the start transform with the give index. + * \param rescue_pos Index of the start position to be returned. + * \returns The transform of the corresponding start position. + */ +btTransform World::getRescueTransform(unsigned int rescue_pos) const +{ + return m_track->getStartTransform(rescue_pos); +} // getRescueTransform + +//----------------------------------------------------------------------------- +/** Uses the start position as rescue positions, override if necessary + */ +unsigned int World::getNumberOfRescuePositions() const +{ + return m_track->getNumberOfStartPositions(); +} // getNumberOfRescuePositions /* EOF */ diff --git a/src/modes/world.hpp b/src/modes/world.hpp index 3a03d0bf2..83bd68be0 100644 --- a/src/modes/world.hpp +++ b/src/modes/world.hpp @@ -221,13 +221,13 @@ public: // ------------------------------------------------------------------------ /** Returns the number of rescue positions on a given track and game * mode. */ - virtual unsigned int getNumberOfRescuePositions() const = 0; + virtual unsigned int getNumberOfRescuePositions() const; // ------------------------------------------------------------------------ /** Determines the rescue position index of the specified kart. */ virtual unsigned int getRescuePositionIndex(AbstractKart *kart) = 0; // ------------------------------------------------------------------------ /** Returns the bullet transformation for the specified rescue index. */ - virtual btTransform getRescueTransform(unsigned int index) const = 0; + virtual btTransform getRescueTransform(unsigned int index) const; // ------------------------------------------------------------------------ virtual void moveKartAfterRescue(AbstractKart* kart); // ------------------------------------------------------------------------ diff --git a/src/modes/world_with_rank.cpp b/src/modes/world_with_rank.cpp index 6ce2814be..1f40f2885 100644 --- a/src/modes/world_with_rank.cpp +++ b/src/modes/world_with_rank.cpp @@ -124,16 +124,6 @@ void WorldWithRank::endSetKartPositions() #endif } // endSetKartPositions - -//----------------------------------------------------------------------------- -/** WorldWithRank uses the start position as rescue positions. So return - * the number of start positions. - */ -unsigned int WorldWithRank::getNumberOfRescuePositions() const -{ - return getTrack()->getNumberOfStartPositions(); -} // getNumberOfRescuePositions - //----------------------------------------------------------------------------- /** Determines the rescue position for a kart. The rescue position is the * start position which is has the biggest accumulated distance to all other @@ -184,16 +174,6 @@ unsigned int WorldWithRank::getRescuePositionIndex(AbstractKart *kart) return furthest_id_found; } // getRescuePositionIndex -// ---------------------------------------------------------------------------- -/** Returns the start transform with the give index. - * \param rescue_pos Index of the start position to be returned. - * \returns The transform of the corresponding start position. - */ -btTransform WorldWithRank::getRescueTransform(unsigned int rescue_pos) const -{ - return getTrack()->getStartTransform(rescue_pos); -} // getRescueTransform - //----------------------------------------------------------------------------- /** Returns the number of points for a kart at a specified position. * \param p Position (starting with 1). diff --git a/src/modes/world_with_rank.hpp b/src/modes/world_with_rank.hpp index 7c159be89..55495856b 100644 --- a/src/modes/world_with_rank.hpp +++ b/src/modes/world_with_rank.hpp @@ -71,17 +71,11 @@ public: void beginSetKartPositions(); bool setKartPosition(unsigned int kart_id, - unsigned int position); + unsigned int position); void endSetKartPositions(); AbstractKart* getKartAtPosition(unsigned int p) const; virtual int getScoreForPosition(int p); - - - virtual unsigned int getNumberOfRescuePositions() const OVERRIDE; virtual unsigned int getRescuePositionIndex(AbstractKart *kart) OVERRIDE; - virtual btTransform getRescueTransform(unsigned int index) const OVERRIDE; - - }; // WorldWithRank From 100c23b3c0e8487e918e13965089a4bf24b58ce7 Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 28 Sep 2016 13:48:32 +0800 Subject: [PATCH 238/350] Move TrackSector to WorldWithRank --- src/karts/controller/arena_ai.cpp | 3 +- src/karts/controller/arena_ai.hpp | 2 +- src/karts/controller/battle_ai.cpp | 11 ++--- src/karts/controller/battle_ai.hpp | 2 +- src/karts/controller/soccer_ai.cpp | 14 +++--- src/karts/controller/soccer_ai.hpp | 2 +- src/karts/kart.cpp | 3 +- src/modes/linear_world.cpp | 42 ++++++------------ src/modes/linear_world.hpp | 35 +-------------- src/modes/soccer_world.cpp | 44 +------------------ src/modes/soccer_world.hpp | 7 --- src/modes/three_strikes_battle.cpp | 53 +---------------------- src/modes/three_strikes_battle.hpp | 8 ---- src/modes/world_with_rank.cpp | 69 ++++++++++++++++++++++++++++++ src/modes/world_with_rank.hpp | 21 +++++++++ 15 files changed, 127 insertions(+), 189 deletions(-) diff --git a/src/karts/controller/arena_ai.cpp b/src/karts/controller/arena_ai.cpp index 4055f7a6d..ba0f0efd3 100644 --- a/src/karts/controller/arena_ai.cpp +++ b/src/karts/controller/arena_ai.cpp @@ -544,8 +544,7 @@ void ArenaAI::handleArenaItems(const float dt) // Compensate the distance because this distance is straight to straight // in graph node, so if kart to kart are not facing like so as, their real // distance maybe smaller - const float dist_to_kart = - getKartDistance(m_closest_kart->getWorldKartId()) * 0.8f; + const float dist_to_kart = getKartDistance(m_closest_kart) * 0.8f; switch(m_kart->getPowerup()->getType()) { diff --git a/src/karts/controller/arena_ai.hpp b/src/karts/controller/arena_ai.hpp index d314458c6..b0e6ea7d2 100644 --- a/src/karts/controller/arena_ai.hpp +++ b/src/karts/controller/arena_ai.hpp @@ -131,7 +131,7 @@ private: virtual void findTarget() = 0; virtual bool forceBraking() { return m_avoiding_item; } virtual int getCurrentNode() const = 0; - virtual float getKartDistance(int to_id) const = 0; + virtual float getKartDistance(const AbstractKart* kart) const = 0; virtual bool ignorePathFinding() { return false; } virtual bool isWaiting() const = 0; virtual bool isKartOnRoad() const = 0; diff --git a/src/karts/controller/battle_ai.cpp b/src/karts/controller/battle_ai.cpp index b960f17b7..f3cfe7aa7 100644 --- a/src/karts/controller/battle_ai.cpp +++ b/src/karts/controller/battle_ai.cpp @@ -114,7 +114,7 @@ void BattleAI::findClosestKart(bool use_difficulty) } float dist_to_kart = m_graph->getDistance(getCurrentNode(), - m_world->getKartNode(kart->getWorldKartId())); + m_world->getSectorForKart(kart)); if (dist_to_kart <= distance) { distance = dist_to_kart; @@ -123,7 +123,7 @@ void BattleAI::findClosestKart(bool use_difficulty) } m_closest_kart = m_world->getKart(closest_kart_num); - m_closest_kart_node = m_world->getKartNode(closest_kart_num); + m_closest_kart_node = m_world->getSectorForKart(m_closest_kart); m_closest_kart_point = m_closest_kart->getXYZ(); } // findClosestKart @@ -145,7 +145,7 @@ void BattleAI::findTarget() //----------------------------------------------------------------------------- int BattleAI::getCurrentNode() const { - return m_world->getKartNode(m_kart->getWorldKartId()); + return m_world->getSectorForKart(m_kart); } // getCurrentNode //----------------------------------------------------------------------------- @@ -155,9 +155,10 @@ bool BattleAI::isWaiting() const } // isWaiting //----------------------------------------------------------------------------- -float BattleAI::getKartDistance(int to_id) const +float BattleAI::getKartDistance(const AbstractKart* kart) const { - return m_graph->getDistance(getCurrentNode(), m_world->getKartNode(to_id)); + return m_graph->getDistance(getCurrentNode(), + m_world->getSectorForKart(kart)); } // getKartDistance //----------------------------------------------------------------------------- diff --git a/src/karts/controller/battle_ai.hpp b/src/karts/controller/battle_ai.hpp index bb71ad65e..5a89a5112 100644 --- a/src/karts/controller/battle_ai.hpp +++ b/src/karts/controller/battle_ai.hpp @@ -37,7 +37,7 @@ private: virtual void findClosestKart(bool use_difficulty) OVERRIDE; virtual void findTarget() OVERRIDE; virtual int getCurrentNode() const OVERRIDE; - virtual float getKartDistance(int to_id) const OVERRIDE; + virtual float getKartDistance(const AbstractKart* kart) const OVERRIDE; virtual bool isKartOnRoad() const OVERRIDE; virtual bool isWaiting() const OVERRIDE; public: diff --git a/src/karts/controller/soccer_ai.cpp b/src/karts/controller/soccer_ai.cpp index 150b7dd19..ac20eadbb 100644 --- a/src/karts/controller/soccer_ai.cpp +++ b/src/karts/controller/soccer_ai.cpp @@ -157,7 +157,7 @@ void SoccerAI::findClosestKart(bool use_difficulty) } m_closest_kart = m_world->getKart(closest_kart_num); - m_closest_kart_node = m_world->getKartNode(closest_kart_num); + m_closest_kart_node = m_world->getSectorForKart(m_closest_kart); m_closest_kart_point = m_closest_kart->getXYZ(); } // findClosestKart @@ -187,8 +187,9 @@ void SoccerAI::findTarget() { // This AI will attack the other team ball chaser int id = m_world->getBallChaser(m_opp_team); - m_target_point = m_world->getKart(id)->getXYZ(); - m_target_node = m_world->getKartNode(id); + const AbstractKart* kart = m_world->getKart(id); + m_target_point = kart->getXYZ(); + m_target_node = m_world->getSectorForKart(kart); } else { @@ -453,7 +454,7 @@ float SoccerAI::rotateSlope(float old_slope, bool rotate_up) //----------------------------------------------------------------------------- int SoccerAI::getCurrentNode() const { - return m_world->getKartNode(m_kart->getWorldKartId()); + return m_world->getSectorForKart(m_kart); } // getCurrentNode //----------------------------------------------------------------------------- @@ -463,9 +464,10 @@ bool SoccerAI::isWaiting() const } // isWaiting //----------------------------------------------------------------------------- -float SoccerAI::getKartDistance(int to_id) const +float SoccerAI::getKartDistance(const AbstractKart* kart) const { - return m_graph->getDistance(getCurrentNode(), m_world->getKartNode(to_id)); + return m_graph->getDistance(getCurrentNode(), + m_world->getSectorForKart(kart)); } // getKartDistance //----------------------------------------------------------------------------- diff --git a/src/karts/controller/soccer_ai.hpp b/src/karts/controller/soccer_ai.hpp index fe6e00ae6..83bb4e21b 100644 --- a/src/karts/controller/soccer_ai.hpp +++ b/src/karts/controller/soccer_ai.hpp @@ -70,7 +70,7 @@ private: virtual bool forceBraking() OVERRIDE { return m_avoiding_item || m_force_brake; } virtual int getCurrentNode() const OVERRIDE; - virtual float getKartDistance(int to_id) const OVERRIDE; + virtual float getKartDistance(const AbstractKart* kart) const OVERRIDE; virtual bool ignorePathFinding() OVERRIDE { return m_overtake_ball || m_chasing_ball; } virtual bool isKartOnRoad() const OVERRIDE; diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index d3ab47c42..07d4804a1 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -68,6 +68,7 @@ #include "tracks/drive_node.hpp" #include "tracks/track.hpp" #include "tracks/track_manager.hpp" +#include "tracks/track_sector.hpp" #include "utils/constants.hpp" #include "utils/log.hpp" //TODO: remove after debugging is done #include "utils/vs.hpp" @@ -1306,7 +1307,7 @@ void Kart::update(float dt) if (lw && DriveGraph::get()) { const int sector = - lw->getTrackSector(getWorldKartId()).getCurrentGraphNode(); + lw->getTrackSector(getWorldKartId())->getCurrentGraphNode(); dist_to_sector = getXYZ().distance (DriveGraph::get()->getNode(sector)->getCenter()); diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index 9846702a7..8432ccc2f 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -94,7 +94,6 @@ void LinearWorld::reset() for(unsigned int i=0; iupdate(m_karts[i]->getXYZ()); m_karts[i]->setWrongwayCounter(0); } // next kart @@ -181,12 +180,12 @@ void LinearWorld::update(float dt) // in the position of the kart (e.g. while falling the kart // might get too close to another part of the track, shortly // jump to position one, then on reset fall back to last) - if ((!kart_info.getTrackSector()->isOnRoad() && + if ((!getTrackSector(n)->isOnRoad() && (!kart->getMaterial() || kart->getMaterial()->isDriveReset())) && !kart->isGhostKart()) continue; - kart_info.getTrackSector()->update(kart->getFrontXYZ()); + getTrackSector(n)->update(kart->getFrontXYZ()); kart_info.m_overall_distance = kart_info.m_race_lap * m_track->getTrackLength() + getDistanceDownTrackForKart(kart->getWorldKartId()); @@ -380,24 +379,6 @@ void LinearWorld::newLap(unsigned int kart_index) kart->getController()->newLap(kart_info.m_race_lap); } // newLap -//----------------------------------------------------------------------------- -/** Gets the sector a kart is on. This function returns UNKNOWN_SECTOR if the - * kart_id is larger than the current kart info. This is necessary in the case - * that a collision with the track happens during resetAllKarts: at this time - * m_kart_info is not initialised (and has size 0), so it would trigger this - * assert. While this normally does not happen, it is useful for track - * designers that STK does not crash. - * \param kart_id The world kart id of the kart for which to return - * the sector. - */ -int LinearWorld::getSectorForKart(const AbstractKart *kart) const -{ - if(kart->getWorldKartId()>=m_kart_info.size()) - return Graph::UNKNOWN_SECTOR; - return m_kart_info[kart->getWorldKartId()].getTrackSector() - ->getCurrentGraphNode(); -} // getSectorForKart - //----------------------------------------------------------------------------- /** Returns the distance the kart has travelled along the track since * crossing the start line.. @@ -405,8 +386,7 @@ int LinearWorld::getSectorForKart(const AbstractKart *kart) const */ float LinearWorld::getDistanceDownTrackForKart(const int kart_id) const { - assert(kart_id < (int)m_kart_info.size()); - return m_kart_info[kart_id].getTrackSector()->getDistanceFromStart(); + return getTrackSector(kart_id)->getDistanceFromStart(); } // getDistanceDownTrackForKart //----------------------------------------------------------------------------- @@ -416,8 +396,7 @@ float LinearWorld::getDistanceDownTrackForKart(const int kart_id) const */ float LinearWorld::getDistanceToCenterForKart(const int kart_id) const { - assert(kart_id < (int)m_kart_info.size()); - return m_kart_info[kart_id].getTrackSector()->getDistanceToCenter(); + return getTrackSector(kart_id)->getDistanceToCenter(); } // getDistanceToCenterForKart //----------------------------------------------------------------------------- @@ -635,13 +614,13 @@ unsigned int LinearWorld::getNumberOfRescuePositions() const // ------------------------------------------------------------------------ unsigned int LinearWorld::getRescuePositionIndex(AbstractKart *kart) { - KartInfo& info = m_kart_info[kart->getWorldKartId()]; + const unsigned int kart_id = kart->getWorldKartId(); - info.getTrackSector()->rescue(); + getTrackSector(kart_id)->rescue(); // Setting XYZ for the kart is important since otherwise the kart // will not detect the right material again when doing the next // raycast to detect where it is driving on (--> potential rescue loop) - return info.getTrackSector()->getCurrentGraphNode(); + return getTrackSector(kart_id)->getCurrentGraphNode(); } // getRescuePositionIndex // ------------------------------------------------------------------------ @@ -864,7 +843,7 @@ void LinearWorld::checkForWrongDirection(unsigned int i, float dt) // If the kart can go in more than one directions from the current track // don't do any reverse message handling, since it is likely that there // will be one direction in which it isn't going backwards anyway. - int sector = m_kart_info[i].getTrackSector()->getCurrentGraphNode(); + int sector = getTrackSector(i)->getCurrentGraphNode(); if (DriveGraph::get()->getNumberOfSuccessors(sector) > 1) return; @@ -915,3 +894,8 @@ void LinearWorld::checkForWrongDirection(unsigned int i, float dt) } // checkForWrongDirection //----------------------------------------------------------------------------- +void LinearWorld::setLastTriggeredCheckline(unsigned int kart_index, int index) +{ + if (m_kart_info.size() == 0) return; + getTrackSector(kart_index)->setLastTriggeredCheckline(index); +} // setLastTriggeredCheckline diff --git a/src/modes/linear_world.hpp b/src/modes/linear_world.hpp index 495be7b7d..9137b8f72 100644 --- a/src/modes/linear_world.hpp +++ b/src/modes/linear_world.hpp @@ -21,7 +21,6 @@ #include #include "modes/world_with_rank.hpp" -#include "tracks/track_sector.hpp" #include "utils/aligned_array.hpp" class SFXBase; @@ -78,9 +77,6 @@ private: * track-length plus distance-along-track). */ float m_overall_distance; - /** Stores the current graph node and track coordinates etc. */ - TrackSector m_track_sector; - /** Initialises all fields. */ KartInfo() { reset(); } // -------------------------------------------------------------------- @@ -92,14 +88,7 @@ private: m_time_at_last_lap = 99999.9f; m_estimated_finish = -1.0f; m_overall_distance = 0.0f; - m_track_sector.reset(); } // reset - // -------------------------------------------------------------------- - /** Returns a pointer to the current node object. */ - TrackSector *getTrackSector() {return &m_track_sector; } - // -------------------------------------------------------------------- - /** Returns a pointer to the current node object. */ - const TrackSector *getTrackSector() const {return &m_track_sector; } }; // ------------------------------------------------------------------------ @@ -124,7 +113,6 @@ public: virtual ~LinearWorld(); virtual void update(float delta) OVERRIDE; - int getSectorForKart(const AbstractKart *kart) const; float getDistanceDownTrackForKart(const int kart_id) const; float getDistanceToCenterForKart(const int kart_id) const; float getEstimatedFinishTime(const int kart_id) const; @@ -149,14 +137,6 @@ public: // ------------------------------------------------------------------------ /** Override settings from base class */ virtual bool useChecklineRequirements() const OVERRIDE { return true; } - // ------------------------------------------------------------------------ - /** Returns true if the kart is on a valid driveline quad. - * \param kart_index Index of the kart. */ - bool isOnRoad(unsigned int kart_index) const - { - return m_kart_info[kart_index].getTrackSector()->isOnRoad(); - } // isOnRoad - // ------------------------------------------------------------------------ /** Returns the number of laps a kart has completed. * \param kart_index World index of the kart. */ @@ -165,21 +145,8 @@ public: assert(kart_index < m_kart_info.size()); return m_kart_info[kart_index].m_race_lap; } // getkartLap - // ------------------------------------------------------------------------ - /** Returns the track_sector object for the specified kart. - * \param kart_index World index of the kart. */ - TrackSector& getTrackSector(unsigned int kart_index) - { - return m_kart_info[kart_index].m_track_sector; - } // getTrackSector - // ------------------------------------------------------------------------ - void setLastTriggeredCheckline(unsigned int kart_index, int index) - { - if (m_kart_info.size() == 0) - return; - m_kart_info[kart_index].m_track_sector.setLastTriggeredCheckline(index); - } + void setLastTriggeredCheckline(unsigned int kart_index, int index); // ------------------------------------------------------------------------ /** Returns how far the kart has driven so far (i.e. * number-of-laps-finished times track-length plus distance-on-track. diff --git a/src/modes/soccer_world.cpp b/src/modes/soccer_world.cpp index 96f4c16d6..b7f565805 100644 --- a/src/modes/soccer_world.cpp +++ b/src/modes/soccer_world.cpp @@ -71,12 +71,6 @@ SoccerWorld::~SoccerWorld() delete m_ball_track_sector; m_ball_track_sector = NULL; - for (unsigned int i = 0; i < m_kart_track_sector.size(); i++) - { - delete m_kart_track_sector[i]; - } - m_kart_track_sector.clear(); - } // ~SoccerWorld //----------------------------------------------------------------------------- @@ -98,10 +92,8 @@ void SoccerWorld::init() if (m_track->hasNavMesh()) { - // Init track sector if navmesh is found + // Init track sector for ball if navmesh is found m_ball_track_sector = new TrackSector(); - for (unsigned int i = 0; i < m_karts.size(); i++) - m_kart_track_sector.push_back(new TrackSector()); } TrackObjectManager* tom = getTrack()->getTrackObjectManager(); @@ -160,8 +152,6 @@ void SoccerWorld::reset() if (m_track->hasNavMesh()) { m_ball_track_sector->reset(); - for (unsigned int i = 0; i < m_karts.size(); i++) - m_kart_track_sector[i]->reset(); } initKartList(); @@ -192,7 +182,7 @@ void SoccerWorld::update(float dt) updateBallPosition(dt); if (m_track->hasNavMesh()) { - updateKartNodes(); + updateSectorForKarts(); updateAIData(); } @@ -450,22 +440,6 @@ AbstractKart *SoccerWorld::createKart(const std::string &kart_ident, int index, return new_kart; } // createKart -//----------------------------------------------------------------------------- -/** Updates the m_kart_on_node value of each kart to localize it - * on the navigation mesh. - */ -void SoccerWorld::updateKartNodes() -{ - if (isRaceOver()) return; - - const unsigned int n = getNumKarts(); - for (unsigned int i = 0; i < n; i++) - { - if (m_karts[i]->isEliminated()) continue; - m_kart_track_sector[i]->update(m_karts[i]->getXYZ()); - } -} // updateKartNodes - //----------------------------------------------------------------------------- /** Localize the ball on the navigation mesh. */ @@ -504,13 +478,6 @@ void SoccerWorld::updateBallPosition(float dt) } // updateBallPosition -//----------------------------------------------------------------------------- -int SoccerWorld::getKartNode(unsigned int kart_id) const -{ - assert(kart_id < m_kart_track_sector.size()); - return m_kart_track_sector[kart_id]->getCurrentGraphNode(); -} // getKartNode - //----------------------------------------------------------------------------- int SoccerWorld::getBallNode() const { @@ -756,10 +723,3 @@ void SoccerWorld::setAITeam() Log::debug("SoccerWorld","blue AI: %d red AI: %d", m_blue_ai, m_red_ai); } // setAITeam - -//----------------------------------------------------------------------------- -bool SoccerWorld::isOnRoad(unsigned int kart_id) const -{ - assert(m_kart_track_sector.size() > kart_id); - return m_kart_track_sector[kart_id]->isOnRoad(); -} // isOnRoad diff --git a/src/modes/soccer_world.hpp b/src/modes/soccer_world.hpp index 8cb9c4c33..dc0abadce 100644 --- a/src/modes/soccer_world.hpp +++ b/src/modes/soccer_world.hpp @@ -280,7 +280,6 @@ private: std::map m_kart_position_map; /** Data generated from navmesh */ - std::vector m_kart_track_sector; TrackSector* m_ball_track_sector; int m_red_ai; @@ -290,8 +289,6 @@ private: /** Set the team for the karts */ void initKartList(); - /** Function to update the locations of all karts on the polygon map */ - void updateKartNodes(); /** Function to update the location the ball on the polygon map */ void updateBallPosition(float dt); /** Function to update data for AI usage. */ @@ -356,10 +353,6 @@ public: m_blue_score_times : m_red_score_times); } // ------------------------------------------------------------------------ - int getKartNode(unsigned int kart_id) const; - // ------------------------------------------------------------------------ - bool isOnRoad(unsigned int kart_id) const; - // ------------------------------------------------------------------------ int getBallNode() const; // ------------------------------------------------------------------------ const Vec3& getBallPosition() const diff --git a/src/modes/three_strikes_battle.cpp b/src/modes/three_strikes_battle.cpp index d717b7201..f8bf91434 100644 --- a/src/modes/three_strikes_battle.cpp +++ b/src/modes/three_strikes_battle.cpp @@ -30,7 +30,6 @@ #include "states_screens/race_gui_base.hpp" #include "tracks/track.hpp" #include "tracks/track_object_manager.hpp" -#include "tracks/track_sector.hpp" #include "utils/constants.hpp" #include @@ -65,12 +64,6 @@ void ThreeStrikesBattle::init() WorldWithRank::init(); m_display_rank = false; m_kart_info.resize(m_karts.size()); - if (m_track->hasNavMesh()) - { - // Init track sector if navmesh is found - for (unsigned int i = 0; i < m_karts.size(); i++) - m_kart_track_sector.push_back(new TrackSector()); - } } // ThreeStrikesBattle //----------------------------------------------------------------------------- @@ -86,12 +79,6 @@ ThreeStrikesBattle::~ThreeStrikesBattle() // freed once all refernces to it (which will happen once all // karts are being freed, which would have a pointer to this mesh) irr_driver->removeMeshFromCache(m_tire); - - for (unsigned int i = 0; i < m_kart_track_sector.size(); i++) - { - delete m_kart_track_sector[i]; - } - m_kart_track_sector.clear(); } // ~ThreeStrikesBattle //----------------------------------------------------------------------------- @@ -145,12 +132,6 @@ void ThreeStrikesBattle::reset() m_track->getTrackObjectManager()->removeObject(obj); } m_tires.clearWithoutDeleting(); - - if (m_track->hasNavMesh()) - { - for (unsigned int i = 0; i < kart_amount; i++) - m_kart_track_sector[i]->reset(); - } } // reset //----------------------------------------------------------------------------- @@ -325,7 +306,7 @@ void ThreeStrikesBattle::update(float dt) WorldWithRank::updateTrack(dt); if (m_track->hasNavMesh()) - updateKartNodes(); + updateSectorForKarts(); // insert blown away tire(s) now if was requested while (m_insert_tire > 0) @@ -475,31 +456,6 @@ bool ThreeStrikesBattle::isRaceOver() return getCurrentNumKarts()==1 || getCurrentNumPlayers()==0; } // isRaceOver -//----------------------------------------------------------------------------- -/** Updates the m_on_node value of each kart to localize it - * on the navigation mesh. - */ -void ThreeStrikesBattle::updateKartNodes() -{ - if (isRaceOver()) return; - - const unsigned int n = getNumKarts(); - for (unsigned int i = 0; i < n; i++) - { - if (m_karts[i]->isEliminated()) continue; - m_kart_track_sector[i]->update(m_karts[i]->getXYZ()); - } -} // updateKartNodes - -//----------------------------------------------------------------------------- -/** Get the which node the kart located in navigation mesh. - */ -int ThreeStrikesBattle::getKartNode(unsigned int kart_id) const -{ - assert(kart_id < m_kart_track_sector.size()); - return m_kart_track_sector[kart_id]->getCurrentGraphNode(); -} // getKartNode - //----------------------------------------------------------------------------- /** Called when the race finishes, i.e. after playing (if necessary) an * end of race animation. It updates the time for all karts still racing, @@ -565,10 +521,3 @@ void ThreeStrikesBattle::enterRaceOverState() } } // enterRaceOverState - -//----------------------------------------------------------------------------- -bool ThreeStrikesBattle::isOnRoad(unsigned int kart_id) const -{ - assert(m_kart_track_sector.size() > kart_id); - return m_kart_track_sector[kart_id]->isOnRoad(); -} // isOnRoad diff --git a/src/modes/three_strikes_battle.hpp b/src/modes/three_strikes_battle.hpp index 24a6dee10..1c920b007 100644 --- a/src/modes/three_strikes_battle.hpp +++ b/src/modes/three_strikes_battle.hpp @@ -29,7 +29,6 @@ #include class PhysicalObject; -class TrackSector; /** * \brief An implementation of World, to provide the 3 strikes battle game mode @@ -71,11 +70,6 @@ private: PtrVector m_tires; - std::vector m_kart_track_sector; - - /** Function to update the locations of all karts on the navigation map */ - void updateKartNodes(); - /** Profiling usage */ int m_total_rescue; int m_frame_count; @@ -118,8 +112,6 @@ public: virtual void kartAdded(AbstractKart* kart, scene::ISceneNode* node) OVERRIDE; virtual void enterRaceOverState() OVERRIDE; - int getKartNode(unsigned int kart_id) const; - bool isOnRoad(unsigned int kart_id) const; void updateKartRanks(); void increaseRescueCount() { m_total_rescue++; } }; // ThreeStrikesBattles diff --git a/src/modes/world_with_rank.cpp b/src/modes/world_with_rank.cpp index 1f40f2885..e3badf672 100644 --- a/src/modes/world_with_rank.cpp +++ b/src/modes/world_with_rank.cpp @@ -20,11 +20,23 @@ #include "karts/abstract_kart.hpp" #include "karts/kart_properties.hpp" #include "race/history.hpp" +#include "tracks/graph.hpp" #include "tracks/track.hpp" +#include "tracks/track_sector.hpp" #include "utils/log.hpp" #include +//----------------------------------------------------------------------------- +WorldWithRank::~WorldWithRank() +{ + for (unsigned int i = 0; i < m_kart_track_sector.size(); i++) + { + delete m_kart_track_sector[i]; + } + m_kart_track_sector.clear(); +} // ~WorldWithRank + //----------------------------------------------------------------------------- void WorldWithRank::init() { @@ -39,8 +51,23 @@ void WorldWithRank::init() #endif stk_config->getAllScores(&m_score_for_position, getNumKarts()); + // Don't init track sector if navmesh is not found in arena + if ((m_track->isArena() || m_track->isSoccer()) && !m_track->hasNavMesh()) + return; + + for (unsigned int i = 0; i < m_karts.size(); i++) + m_kart_track_sector.push_back(new TrackSector()); + } // init +//----------------------------------------------------------------------------- +void WorldWithRank::reset() +{ + World::reset(); + for (unsigned int i = 0; i < m_kart_track_sector.size(); i++) + getTrackSector(i)->update(m_karts[i]->getXYZ()); +} // reset + //----------------------------------------------------------------------------- /** Returns the kart with a given position. * \param p The position of the kart, 1<=p<=num_karts). @@ -184,3 +211,45 @@ int WorldWithRank::getScoreForPosition(int p) assert(p - 1 <(int) m_score_for_position.size()); return m_score_for_position[p - 1]; } // getScoreForPosition + +//----------------------------------------------------------------------------- +/** Returns true if the kart is on a valid graph quad. + * \param kart_index Index of the kart. + */ +bool WorldWithRank::isOnRoad(unsigned int kart_index) const +{ + return getTrackSector(kart_index)->isOnRoad(); +} // isOnRoad + +//----------------------------------------------------------------------------- +/** Gets the sector a kart is on. This function returns UNKNOWN_SECTOR if the + * kart_id is larger than the current kart sector. This is necessary in the + * case that a collision with the track happens during resetAllKarts: at this + * time m_kart_track_sector is not initialised (and has size 0), so it would + * trigger this assert. While this normally does not happen, it is useful for + * track designers that STK does not crash. + * \param kart_id The world kart id of the kart for which to return + * the sector. + */ +int WorldWithRank::getSectorForKart(const AbstractKart *kart) const +{ + if (kart->getWorldKartId() >= m_kart_track_sector.size()) + return Graph::UNKNOWN_SECTOR; + return getTrackSector(kart->getWorldKartId())->getCurrentGraphNode(); +} // getSectorForKart + +//----------------------------------------------------------------------------- +/** Localize each kart on the graph using its center xyz. + */ +void WorldWithRank::updateSectorForKarts() +{ + if (isRaceOver()) return; + + const unsigned int n = getNumKarts(); + assert(n == m_kart_track_sector.size()); + for (unsigned int i = 0; i < n; i++) + { + if (m_karts[i]->isEliminated()) continue; + getTrackSector(i)->update(m_karts[i]->getXYZ()); + } +} // updateSectorForKarts diff --git a/src/modes/world_with_rank.hpp b/src/modes/world_with_rank.hpp index 55495856b..fcf8d1a6e 100644 --- a/src/modes/world_with_rank.hpp +++ b/src/modes/world_with_rank.hpp @@ -23,6 +23,7 @@ #include "modes/world.hpp" class AbstractKart; +class TrackSector; /** * A WorldWithRank is a world where the karts are ranked. This is the base @@ -60,12 +61,20 @@ protected: unsigned int getClosestStartPoint(AbstractKart *kart); + /** Stores the current graph node and track coordinates for each kart. */ + std::vector m_kart_track_sector; + + // ------------------------------------------------------------------------ + void updateSectorForKarts(); + public: WorldWithRank() : World() {} + virtual ~WorldWithRank(); /** call just after instanciating. can't be moved to the contructor as child classes must be instanciated, otherwise polymorphism will fail and the results will be incorrect */ virtual void init() OVERRIDE; + virtual void reset() OVERRIDE; bool displayRank() const { return m_display_rank; } @@ -76,6 +85,18 @@ public: AbstractKart* getKartAtPosition(unsigned int p) const; virtual int getScoreForPosition(int p); virtual unsigned int getRescuePositionIndex(AbstractKart *kart) OVERRIDE; + // ------------------------------------------------------------------------ + /** Returns the track_sector object for the specified kart. + * \param kart_index World index of the kart. */ + TrackSector* getTrackSector(unsigned int kart_index) const + { + assert(kart_index < m_kart_track_sector.size()); + return m_kart_track_sector[kart_index]; + } // getTrackSector + // ------------------------------------------------------------------------ + bool isOnRoad(unsigned int kart_index) const; + // ------------------------------------------------------------------------ + int getSectorForKart(const AbstractKart *kart) const; }; // WorldWithRank From df52922d0873dca23562203be2152f184f6cad16 Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 29 Sep 2016 09:57:38 +0800 Subject: [PATCH 239/350] Clean up unused function and wrong comment --- src/items/item.hpp | 22 +--------------------- src/karts/controller/skidding_ai.cpp | 4 ---- 2 files changed, 1 insertion(+), 25 deletions(-) diff --git a/src/items/item.hpp b/src/items/item.hpp index 0654d2ea7..eadd653d1 100644 --- a/src/items/item.hpp +++ b/src/items/item.hpp @@ -183,7 +183,6 @@ public: const AbstractKart* getEmitter() const { return m_emitter; } - // ------------------------------------------------------------------------ /** Returns true if the Kart is close enough to hit this item, the item is * not deactivated anymore, and it wasn't placed by this kart (this is @@ -192,31 +191,12 @@ public: * \param xyz Location of kart (avoiding to use kart->getXYZ() so that * kart.hpp does not need to be included here). */ - bool hitKart (const Vec3 &xyz, const AbstractKart *kart=NULL) const + bool hitKart(const Vec3 &xyz, const AbstractKart *kart=NULL) const { return (m_event_handler!=kart || m_deactive_time <=0) && (xyz-m_xyz).length2()getXYZ() so that - * kart.hpp does not need to be included here). - */ - bool hitKart (const core::vector2df &xyz, - const AbstractKart *kart=NULL) const - { - if(m_event_handler==kart && m_deactive_time >0) return false; - float d2 = (m_xyz.getX()-xyz.X)*(m_xyz.getX()-xyz.X) - + (m_xyz.getZ()-xyz.Y)*(m_xyz.getZ()-xyz.Y); - return d2 < m_distance_2; - } // hitKart - protected: // ------------------------------------------------------------------------ // Some convenient functions for the AI only diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index bbdadd653..6f1f3e253 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -1081,10 +1081,6 @@ void SkiddingAI::evaluateItems(const Item *item, Vec3 kart_aim_direction, // to avoid are collected). if(!avoid) { - // Project the item's location onto the plane of the current quad. - // This is necessary because the kart's aim point may not be on the track - // in 3D curves. So we project the item's location onto the plane in which - // the kart is. The current quad provides a good estimate of the kart's plane. const Vec3 &xyz = item->getXYZ(); float angle_to_item = (xyz - m_kart->getXYZ()).angle(kart_aim_direction); From 32f87fa8a9738bac0de0f87f194e0b158d8da14d Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 29 Sep 2016 10:19:18 +0800 Subject: [PATCH 240/350] Fix wrong doxygen comments --- src/modes/soccer_world.hpp | 2 +- src/modes/three_strikes_battle.hpp | 3 ++- src/modes/world_with_rank.cpp | 3 +-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modes/soccer_world.hpp b/src/modes/soccer_world.hpp index dc0abadce..701b848f5 100644 --- a/src/modes/soccer_world.hpp +++ b/src/modes/soccer_world.hpp @@ -33,7 +33,7 @@ class Controller; class TrackObject; class TrackSector; -/** An implementation of World, to provide the soccer game mode +/** \brief An implementation of WorldWithRank, to provide the soccer game mode * Notice: In soccer world, true goal means blue, false means red. * \ingroup modes */ diff --git a/src/modes/three_strikes_battle.hpp b/src/modes/three_strikes_battle.hpp index 1c920b007..0acfffd43 100644 --- a/src/modes/three_strikes_battle.hpp +++ b/src/modes/three_strikes_battle.hpp @@ -31,7 +31,8 @@ class PhysicalObject; /** - * \brief An implementation of World, to provide the 3 strikes battle game mode + * \brief An implementation of WorldWithRank, to provide the 3 strikes battle + * game mode * \ingroup modes */ class ThreeStrikesBattle : public WorldWithRank diff --git a/src/modes/world_with_rank.cpp b/src/modes/world_with_rank.cpp index e3badf672..0c04344c4 100644 --- a/src/modes/world_with_rank.cpp +++ b/src/modes/world_with_rank.cpp @@ -228,8 +228,7 @@ bool WorldWithRank::isOnRoad(unsigned int kart_index) const * time m_kart_track_sector is not initialised (and has size 0), so it would * trigger this assert. While this normally does not happen, it is useful for * track designers that STK does not crash. - * \param kart_id The world kart id of the kart for which to return - * the sector. + * \param kart Kart for which to return the sector. */ int WorldWithRank::getSectorForKart(const AbstractKart *kart) const { From 7f6d800fe7fede12653ac5790084f7afc8795822 Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 29 Sep 2016 15:10:26 +1000 Subject: [PATCH 241/350] Made the boundary box private to Graph. --- src/tracks/drive_graph.cpp | 4 +--- src/tracks/graph.cpp | 3 +++ src/tracks/graph.hpp | 10 +++++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/tracks/drive_graph.cpp b/src/tracks/drive_graph.cpp index 3c86963f9..5f98b1ad8 100644 --- a/src/tracks/drive_graph.cpp +++ b/src/tracks/drive_graph.cpp @@ -122,9 +122,7 @@ void DriveGraph::load(const std::string &quad_file_name, bool ai_ignore=false; xml_node->get("ai-ignore", &ai_ignore); createQuad(p0, p1, p2, p3, m_all_nodes.size(), invisible, ai_ignore, - false/*is_arena*/); - m_bb_max.max(p0);m_bb_max.max(p1);m_bb_max.max(p2);m_bb_max.max(p3); - m_bb_min.min(p0);m_bb_min.min(p1);m_bb_min.min(p2);m_bb_min.min(p3); + false/*is_arena*/); } delete quad; diff --git a/src/tracks/graph.cpp b/src/tracks/graph.cpp index ea9a481c3..7d5b9aadc 100644 --- a/src/tracks/graph.cpp +++ b/src/tracks/graph.cpp @@ -446,6 +446,9 @@ void Graph::createQuad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, } m_all_nodes.push_back(q); + m_bb_max.max(p0); m_bb_max.max(p1); m_bb_max.max(p2); m_bb_max.max(p3); + m_bb_min.min(p0); m_bb_min.min(p1); m_bb_min.min(p2); m_bb_min.min(p3); + } // createQuad //----------------------------------------------------------------------------- diff --git a/src/tracks/graph.hpp b/src/tracks/graph.hpp index ba5e89ac7..c16cc305c 100644 --- a/src/tracks/graph.hpp +++ b/src/tracks/graph.hpp @@ -51,16 +51,16 @@ class RTT; */ class Graph : public NoCopy { +private: + /** The 2d bounding box, used for hashing. */ + Vec3 m_bb_min; + Vec3 m_bb_max; + protected: static Graph* m_graph; std::vector m_all_nodes; - /** The 2d bounding box, used for hashing. */ - Vec3 m_bb_min; - - Vec3 m_bb_max; - // ------------------------------------------------------------------------ /** Factory method to dynamic create 2d / 3d quad for drive and arena * graph. */ From fb7b42a625f1c70e3ea7df87863f33a8ac6df94a Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 29 Sep 2016 15:10:51 +1000 Subject: [PATCH 242/350] Fixed compiler warning. --- src/tracks/track.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index 107ef4f7d..a24d79a6b 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -2290,7 +2290,7 @@ void Track::itemCommand(const XMLNode *node) // If raycast is used, increase the start position slightly // in case that the point is too close to the actual surface // (e.g. floating point errors can cause a problem here). - loc += quad_normal * 0.1; + loc += quad_normal * 0.1f; #ifndef DEBUG m_track_mesh->castRay(loc, loc + (-10000 * quad_normal), &hit_point, From 0b76a576a33d97b87ad8540b55f285d37e89432e Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 29 Sep 2016 15:11:08 +1000 Subject: [PATCH 243/350] Exit loop as soon as kart is found to be on one GraphNode. --- src/tracks/graph.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tracks/graph.cpp b/src/tracks/graph.cpp index 7d5b9aadc..ff9dc9bea 100644 --- a/src/tracks/graph.cpp +++ b/src/tracks/graph.cpp @@ -502,6 +502,7 @@ void Graph::findRoadSector(const Vec3& xyz, int *sector, if(q->pointInside(xyz, ignore_vertical)) { *sector = indx; + return; } } // for i Date: Thu, 29 Sep 2016 15:21:59 +1000 Subject: [PATCH 244/350] Oops - made boundary box protected again, it is accessed in the ArenaGraph. --- src/tracks/graph.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tracks/graph.hpp b/src/tracks/graph.hpp index c16cc305c..ba5e89ac7 100644 --- a/src/tracks/graph.hpp +++ b/src/tracks/graph.hpp @@ -51,16 +51,16 @@ class RTT; */ class Graph : public NoCopy { -private: - /** The 2d bounding box, used for hashing. */ - Vec3 m_bb_min; - Vec3 m_bb_max; - protected: static Graph* m_graph; std::vector m_all_nodes; + /** The 2d bounding box, used for hashing. */ + Vec3 m_bb_min; + + Vec3 m_bb_max; + // ------------------------------------------------------------------------ /** Factory method to dynamic create 2d / 3d quad for drive and arena * graph. */ From 43c40c062ee46b0a9411adad6b4230baf366a09f Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 29 Sep 2016 13:36:50 +0800 Subject: [PATCH 245/350] Make m_bb_max and m_bb_min private in Graph again Avoid setting them in ArenaGraph, let createQuad handle them all --- src/tracks/arena_graph.cpp | 2 -- src/tracks/graph.hpp | 9 ++++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/tracks/arena_graph.cpp b/src/tracks/arena_graph.cpp index d27079bb4..13dbde4ea 100644 --- a/src/tracks/arena_graph.cpp +++ b/src/tracks/arena_graph.cpp @@ -117,8 +117,6 @@ void ArenaGraph::loadNavmesh(const std::string &navmesh) xml_node_node->get("y", &y); xml_node_node->get("z", &z); Vec3 p(x, y, z); - m_bb_min.min(p); - m_bb_max.max(p); all_vertices.push_back(p); } } diff --git a/src/tracks/graph.hpp b/src/tracks/graph.hpp index ba5e89ac7..56297882b 100644 --- a/src/tracks/graph.hpp +++ b/src/tracks/graph.hpp @@ -56,11 +56,6 @@ protected: std::vector m_all_nodes; - /** The 2d bounding box, used for hashing. */ - Vec3 m_bb_min; - - Vec3 m_bb_max; - // ------------------------------------------------------------------------ /** Factory method to dynamic create 2d / 3d quad for drive and arena * graph. */ @@ -69,6 +64,10 @@ protected: bool invisible, bool ai_ignore, bool is_arena); private: + /** The 2d bounding box, used for hashing. */ + Vec3 m_bb_min; + Vec3 m_bb_max; + RTT* m_new_rtt; /** The node of the graph mesh. */ From d386c76e2d4a66a300725f668d783ebc8741dd72 Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 29 Sep 2016 14:37:39 +0800 Subject: [PATCH 246/350] Fix explosion animation on upside down area --- src/karts/explosion_animation.cpp | 6 +++--- src/karts/explosion_animation.hpp | 8 ++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/karts/explosion_animation.cpp b/src/karts/explosion_animation.cpp index 8d5b42c8b..4980fab57 100644 --- a/src/karts/explosion_animation.cpp +++ b/src/karts/explosion_animation.cpp @@ -77,6 +77,7 @@ ExplosionAnimation::ExplosionAnimation(AbstractKart *kart, { m_xyz = m_kart->getXYZ(); m_orig_xyz = m_xyz; + m_normal = m_kart->getNormal(); m_kart->playCustomSFX(SFXManager::CUSTOM_EXPLODE); m_timer = m_kart->getKartProperties()->getExplosionDuration(); @@ -141,11 +142,10 @@ ExplosionAnimation::~ExplosionAnimation() void ExplosionAnimation::update(float dt) { m_velocity -= dt*World::getWorld()->getTrack()->getGravity(); - Vec3 normal = -1.0f*m_kart->getBody()->getGravity().normalized(); - m_xyz = Vec3(m_xyz + dt*m_velocity*normal); + m_xyz = m_xyz + dt*m_velocity*m_normal; // Make sure the kart does not end up under the track - if ((m_xyz - m_orig_xyz).dot(normal)<0) + if ((m_xyz - m_orig_xyz).dot(m_normal)<0) { m_xyz = m_orig_xyz; // This will trigger the end of the animations diff --git a/src/karts/explosion_animation.hpp b/src/karts/explosion_animation.hpp index 572eaaffd..86a317320 100644 --- a/src/karts/explosion_animation.hpp +++ b/src/karts/explosion_animation.hpp @@ -37,14 +37,18 @@ class ExplosionAnimation: public AbstractKartAnimation { protected: - /** The coordinates where the kart was hit originally. */ + /** The coordinates where the kart was hit originally, it will be increased + * later. */ Vec3 m_xyz; - /** The original Y coordinate. The kart needs to be restored accurately + /** The original coordinates. The kart needs to be restored accurately * otherwise due to floating point errors, time step size variations, * a kart can be restarted under the track. */ Vec3 m_orig_xyz; + /** The normal of kart when it started to explode. */ + Vec3 m_normal; + /** The kart's current rotation. */ Vec3 m_curr_rotation; From c22714951cf0ceb2433b515e75220e276178d35d Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 30 Sep 2016 00:42:04 +0800 Subject: [PATCH 247/350] Missed reset before update TrackSector --- src/modes/world_with_rank.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/modes/world_with_rank.cpp b/src/modes/world_with_rank.cpp index 0c04344c4..cb84ae2da 100644 --- a/src/modes/world_with_rank.cpp +++ b/src/modes/world_with_rank.cpp @@ -65,7 +65,10 @@ void WorldWithRank::reset() { World::reset(); for (unsigned int i = 0; i < m_kart_track_sector.size(); i++) + { + getTrackSector(i)->reset(); getTrackSector(i)->update(m_karts[i]->getXYZ()); + } } // reset //----------------------------------------------------------------------------- From 3c24c73faa53ad6cd0ad0e19296af33e61780da2 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 30 Sep 2016 08:15:36 +0800 Subject: [PATCH 248/350] Fix unknown sector in arena after rescue --- src/tracks/track_sector.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tracks/track_sector.cpp b/src/tracks/track_sector.cpp index c6fac42c8..7959213d7 100644 --- a/src/tracks/track_sector.cpp +++ b/src/tracks/track_sector.cpp @@ -55,11 +55,11 @@ void TrackSector::update(const Vec3 &xyz, bool ignore_vertical) const ArenaGraph* ag = ArenaGraph::get(); std::vector* test_nodes = NULL; - if (ag) + if (ag && m_on_road && prev_sector != Graph::UNKNOWN_SECTOR) { - // For ArenaGraph, only test nodes around current node - if (prev_sector != Graph::UNKNOWN_SECTOR) - test_nodes = ag->getNode(prev_sector)->getNearbyNodes(); + // For ArenaGraph, only test nodes around current node if previously + // is on road + test_nodes = ag->getNode(prev_sector)->getNearbyNodes(); } Graph::get()->findRoadSector(xyz, &m_current_graph_node, test_nodes, From 62dfef1fb98eb08f7e11dc227aff753b7ed4973e Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 30 Sep 2016 09:27:44 +0800 Subject: [PATCH 249/350] Improve performance for findOutOfRoadSector on ArenaGraph --- src/tracks/track_sector.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tracks/track_sector.cpp b/src/tracks/track_sector.cpp index 7959213d7..d42e82d89 100644 --- a/src/tracks/track_sector.cpp +++ b/src/tracks/track_sector.cpp @@ -52,18 +52,18 @@ void TrackSector::reset() void TrackSector::update(const Vec3 &xyz, bool ignore_vertical) { int prev_sector = m_current_graph_node; - const ArenaGraph* ag = ArenaGraph::get(); std::vector* test_nodes = NULL; - if (ag && m_on_road && prev_sector != Graph::UNKNOWN_SECTOR) + + if (ag && prev_sector != Graph::UNKNOWN_SECTOR) { - // For ArenaGraph, only test nodes around current node if previously - // is on road + // For ArenaGraph, only test nodes around current node test_nodes = ag->getNode(prev_sector)->getNearbyNodes(); } - Graph::get()->findRoadSector(xyz, &m_current_graph_node, test_nodes, - ignore_vertical); + // Don't only test nodes around if it was not on road + Graph::get()->findRoadSector(xyz, &m_current_graph_node, + m_on_road ? test_nodes : NULL, ignore_vertical); m_on_road = m_current_graph_node != Graph::UNKNOWN_SECTOR; // If m_track_sector == UNKNOWN_SECTOR, then the kart is not on top of From c434cb80f4db6456d8c10fc3375172c95e77c84f Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 1 Oct 2016 07:58:14 +0800 Subject: [PATCH 250/350] Significantly improve turning and bad items avoidance for ArenaAI --- src/karts/controller/ai_base_controller.cpp | 48 +++ src/karts/controller/ai_base_controller.hpp | 8 +- src/karts/controller/arena_ai.cpp | 322 ++++++++------------ src/karts/controller/arena_ai.hpp | 28 +- src/karts/controller/skidding_ai.cpp | 46 --- src/karts/controller/skidding_ai.hpp | 1 - 6 files changed, 198 insertions(+), 255 deletions(-) diff --git a/src/karts/controller/ai_base_controller.cpp b/src/karts/controller/ai_base_controller.cpp index 0db7cce16..7885625d3 100644 --- a/src/karts/controller/ai_base_controller.cpp +++ b/src/karts/controller/ai_base_controller.cpp @@ -279,3 +279,51 @@ void AIBaseController::crashed(const Material *m) } } // crashed(Material) + +// ---------------------------------------------------------------------------- +/** Determine the center point and radius of a circle given two points on + * the circle and the tangent at the first point. This is done as follows: + * 1. Determine the line going through the center point start+end, which is + * orthogonal to the vector from start to end. This line goes through the + * center of the circle. + * 2. Determine the line going through the first point and is orthogonal + * to the given tangent. + * 3. The intersection of these two lines is the center of the circle. + * \param[in] end Second point on circle. + * \param[out] center Center point of the circle (local coordinate). + * \param[out] radius Radius of the circle. + */ +void AIBaseController::determineTurnRadius(const Vec3 &end, Vec3 *center, + float *radius) const +{ + // Convert end point to local coordinate, so start will be 0, 0, 0 + Vec3 lc = m_kart->getTrans().inverse()(end); + + // 1) Line through middle of start+end + Vec3 mid = 0.5f * lc; + Vec3 direction = lc; + + Vec3 orthogonal(direction.getZ(), 0, -direction.getX()); + Vec3 q1 = mid + orthogonal; + irr::core::line2df line1(mid.getX(), mid.getZ(), + q1.getX(), q1.getZ() ); + + irr::core::line2df line2(0, 0, 1, 0); + irr::core::vector2df result; + if (line1.intersectWith(line2, result, /*checkOnlySegments*/false)) + { + Vec3 lc_center(result.X, 0, result.Y); + if (center) + *center = lc_center; + *radius = lc_center.length(); + } + else + { + // No intersection. In this case assume that the two points are + // on a semicircle, in which case the center is at 0.5*(start+end): + if (center) + *center = mid; + *radius = 0.5f*(lc).length(); + } + +} // determineTurnRadius diff --git a/src/karts/controller/ai_base_controller.hpp b/src/karts/controller/ai_base_controller.hpp index fa25cb670..82e07f542 100644 --- a/src/karts/controller/ai_base_controller.hpp +++ b/src/karts/controller/ai_base_controller.hpp @@ -67,13 +67,15 @@ protected: void setControllerName(const std::string &name); float steerToPoint(const Vec3 &point); float normalizeAngle(float angle); - virtual void update (float delta); - virtual void setSteering (float angle, float dt); - virtual bool canSkid(float steer_fraction) = 0; // ------------------------------------------------------------------------ /** This can be called to detect if the kart is stuck (i.e. repeatedly * hitting part of the track). */ bool isStuck() const { return m_stuck; } + void determineTurnRadius(const Vec3 &end, Vec3 *center, + float *radius) const; + virtual void update (float delta); + virtual void setSteering (float angle, float dt); + virtual bool canSkid(float steer_fraction) = 0; public: AIBaseController(AbstractKart *kart); diff --git a/src/karts/controller/arena_ai.cpp b/src/karts/controller/arena_ai.cpp index ba0f0efd3..1be05b4ac 100644 --- a/src/karts/controller/arena_ai.cpp +++ b/src/karts/controller/arena_ai.cpp @@ -62,11 +62,8 @@ void ArenaAI::reset() m_time_since_reversing = 0.0f; m_time_since_uturn = 0.0f; m_turn_radius = 0.0f; - m_turn_angle = 0.0f; m_steering_angle = 0.0f; m_on_node.clear(); - m_aiming_points.clear(); - m_aiming_nodes.clear(); m_cur_difficulty = race_manager->getDifficulty(); AIBaseController::reset(); @@ -116,7 +113,7 @@ void ArenaAI::update(float dt) } checkIfStuck(dt); - if (handleArenaUnstuck(dt)) + if (gettingUnstuck(dt)) return; findClosestKart(true); @@ -126,24 +123,25 @@ void ArenaAI::update(float dt) // u-turn m_target_point_lc = m_kart->getTrans().inverse()(m_target_point); doSkiddingTest(); - handleArenaItems(dt); + configSteering(); + useItems(dt); - if (m_kart->getSpeed() > 15.0f && m_turn_angle < 20) + if (m_kart->getSpeed() > 15.0f && !m_is_uturn && m_turn_radius > 30.0f && + !ignorePathFinding()) { - // Only use nitro when turn angle is big (180 - angle) + // Only use nitro when turn radius is large m_controls->m_nitro = true; } if (m_is_uturn) { resetAfterStop(); - handleArenaUTurn(dt); + doUTurn(dt); } else { - handleArenaAcceleration(dt); - handleArenaSteering(dt); - handleArenaBraking(); + configSpeed(); + setSteering(m_steering_angle, dt); } AIBaseController::update(dt); @@ -151,8 +149,17 @@ void ArenaAI::update(float dt) } // update //----------------------------------------------------------------------------- -bool ArenaAI::updateAimingPosition() +/** Update aiming position, use path finding if necessary, it will set the turn + * radius too. + * \param[out] suitable target point. + * \return true if found a suitable target point. + */ +bool ArenaAI::updateAimingPosition(Vec3* target_point) { +#ifdef AI_DEBUG + m_debug_sphere_next->setVisible(false); +#endif + // Notice: we use the point ahead of kart to determine next node, // to compensate the time difference between steering m_current_forward_point = @@ -168,49 +175,88 @@ bool ArenaAI::updateAimingPosition() test_nodes); // Use current node if forward node is unknown, or near the target - const int forward = (m_current_forward_node == Graph::UNKNOWN_SECTOR || + const int forward = + m_current_forward_node == Graph::UNKNOWN_SECTOR || m_current_forward_node == m_target_node || - getCurrentNode() == m_target_node ? getCurrentNode() : - m_current_forward_node); + getCurrentNode() == m_target_node ? + getCurrentNode() : m_current_forward_node; if (forward == Graph::UNKNOWN_SECTOR || m_target_node == Graph::UNKNOWN_SECTOR) + { + Log::error("ArenaAI", "Next node is unknown, path finding failed!"); + m_turn_radius = 0.0f; return false; + } if (forward == m_target_node) { - m_aiming_points.push_back(m_graph->getNode(forward)->getCenter()); - m_aiming_points.push_back(m_target_point); + if (checkBadItem(&m_target_node)) + m_target_point = m_graph->getNode(m_target_node)->getCenter(); - m_aiming_nodes.insert(forward); - m_aiming_nodes.insert(getCurrentNode()); + determineTurnRadius(m_target_point, NULL, &m_turn_radius); + *target_point = m_target_point; return true; } - const int next_node = m_graph->getNextNode(forward, m_target_node); + int next_node = m_graph->getNextNode(forward, m_target_node); + // Use this next node for aim point if valid if (next_node == Graph::UNKNOWN_SECTOR) { Log::error("ArenaAI", "Next node is unknown, did you forget to link" - "adjacent face in navmesh?"); + " adjacent face in navmesh?"); + m_turn_radius = 0.0f; return false; } - m_aiming_points.push_back(m_graph->getNode(forward)->getCenter()); - m_aiming_points.push_back(m_graph->getNode(next_node)->getCenter()); + checkBadItem(&next_node); + *target_point = m_graph->getNode(next_node)->getCenter(); - m_aiming_nodes.insert(forward); - m_aiming_nodes.insert(next_node); - m_aiming_nodes.insert(getCurrentNode()); + // Then keep getting next node to find the first turning corner which is + // used to determine turn radius + while (m_target_node != next_node) + { + int previous_node = next_node; + next_node = m_graph->getNextNode(previous_node, m_target_node); + if (next_node == Graph::UNKNOWN_SECTOR) + { + Log::error("ArenaAI", "Next node is unknown, did you forget to" + " link adjacent face in navmesh?"); + m_turn_radius = 0.0f; + return false; + } + const Vec3& p1 = m_kart->getXYZ(); + const Vec3& p2 = m_graph->getNode(previous_node)->getCenter(); + const Vec3& p3 = m_graph->getNode(next_node)->getCenter(); + float edge1 = (p1 - p2).length(); + float edge2 = (p2 - p3).length(); + float to_target = (p1 - p3).length(); + + if (fabsf(edge1 + edge2 - to_target) > 0.1f) + { + // Triangle test + determineTurnRadius(p3, NULL, &m_turn_radius); +#ifdef AI_DEBUG + m_debug_sphere_next->setVisible(true); + m_debug_sphere_next->setPosition(p3.toIrrVector()); +#endif + return true; + } + } + + // Target node == next node + determineTurnRadius(m_target_point, NULL, &m_turn_radius); return true; + } // updateAimingPosition //----------------------------------------------------------------------------- -/** This function sets the steering. - * \param dt Time step size. +/** This function config the steering of AI. */ -void ArenaAI::handleArenaSteering(const float dt) +void ArenaAI::configSteering() { + m_steering_angle = 0.0f; const int current_node = getCurrentNode(); if (current_node == Graph::UNKNOWN_SECTOR || @@ -219,12 +265,10 @@ void ArenaAI::handleArenaSteering(const float dt) return; } - m_aiming_points.clear(); - m_aiming_nodes.clear(); - const bool found_position = updateAimingPosition(); if (ignorePathFinding()) { - // Steer directly + // Steer directly, don't brake + m_turn_radius = 100.0f; #ifdef AI_DEBUG m_debug_sphere->setPosition(m_target_point.toIrrVector()); #endif @@ -237,23 +281,20 @@ void ArenaAI::handleArenaSteering(const float dt) else { m_steering_angle = steerToPoint(m_target_point); - setSteering(m_steering_angle, dt); } return; } - else if (found_position) + + // Otherwise use path finding to get target point + Vec3 target_point; + const bool found_position = updateAimingPosition(&target_point); + if (found_position) { - updateBadItemLocation(); - assert(m_aiming_points.size() == 2); - updateTurnRadius(m_kart->getXYZ(), m_aiming_points[0], - m_aiming_points[1]); - m_target_point = m_aiming_points[1]; + m_target_point = target_point; m_target_point_lc = m_kart->getTrans().inverse()(m_target_point); #ifdef AI_DEBUG m_debug_sphere->setVisible(true); - m_debug_sphere_next->setVisible(true); - m_debug_sphere->setPosition(m_aiming_points[0].toIrrVector()); - m_debug_sphere_next->setPosition(m_aiming_points[1].toIrrVector()); + m_debug_sphere->setPosition(m_target_point.toIrrVector()); #endif if (m_target_point_lc.z() < 0) { @@ -263,17 +304,9 @@ void ArenaAI::handleArenaSteering(const float dt) else { m_steering_angle = steerToPoint(m_target_point); - setSteering(m_steering_angle, dt); } - return; } - else - { - // Do nothing (go straight) if no targets found - setSteering(0.0f, dt); - return; - } -} // handleSteering +} // configSteering //----------------------------------------------------------------------------- void ArenaAI::checkIfStuck(const float dt) @@ -311,26 +344,36 @@ void ArenaAI::checkIfStuck(const float dt) } // checkIfStuck //----------------------------------------------------------------------------- -/** Handles acceleration. - * \param dt Time step size. +/** Configure a suitable speed depends on current turn radius. */ -void ArenaAI::handleArenaAcceleration(const float dt) +void ArenaAI::configSpeed() { + m_controls->m_accel = 0.0f; + m_controls->m_brake = false; + // A kart will not brake when the speed is already slower than this + // value. This prevents a kart from going too slow (or even backwards) + // in tight curves. - if (m_controls->m_brake) - { - m_controls->m_accel = 0.0f; - return; - } - + const float MIN_SPEED = 5.0f; const float handicap = (m_cur_difficulty == RaceManager::DIFFICULTY_EASY ? 0.7f : 1.0f); - m_controls->m_accel = stk_config->m_ai_acceleration * handicap; -} // handleArenaAcceleration + const float max_turn_speed = m_kart->getSpeedForTurnRadius(m_turn_radius); + if ((m_kart->getSpeed() > max_turn_speed || forceBraking()) && + m_kart->getSpeed() > MIN_SPEED * handicap) + { + // Brake if necessary + m_controls->m_brake = true; + } + else + { + // Otherwise accelerate + m_controls->m_accel = stk_config->m_ai_acceleration * handicap; + } +} // configSpeed //----------------------------------------------------------------------------- -void ArenaAI::handleArenaUTurn(const float dt) +void ArenaAI::doUTurn(const float dt) { const float turn_side = (m_adjusting_side ? 1.0f : -1.0f); @@ -357,10 +400,10 @@ void ArenaAI::handleArenaUTurn(const float dt) } else m_is_uturn = true; -} // handleArenaUTurn +} // doUTurn //----------------------------------------------------------------------------- -bool ArenaAI::handleArenaUnstuck(const float dt) +bool ArenaAI::gettingUnstuck(const float dt) { if (!m_is_stuck || m_is_uturn) return false; @@ -384,138 +427,41 @@ bool ArenaAI::handleArenaUnstuck(const float dt) AIBaseController::update(dt); return true; -} // handleArenaUnstuck +} // gettingUnstuck //----------------------------------------------------------------------------- -void ArenaAI::updateBadItemLocation() +bool ArenaAI::checkBadItem(int* node) { - // Check if any nodes AI will cross will meet bad items - for (const int& node : m_aiming_nodes) + // Check if the node AI will cross will meet bad items + assert(*node != Graph::UNKNOWN_SECTOR); + Item* selected = ItemManager::get()->getFirstItemInQuad(*node); + + if (selected && !selected->wasCollected() && + (selected->getType() == Item::ITEM_BANANA || + selected->getType() == Item::ITEM_BUBBLEGUM || + selected->getType() == Item::ITEM_BUBBLEGUM_NOLOK)) { - assert(node != Graph::UNKNOWN_SECTOR); - Item* selected = ItemManager::get()->getFirstItemInQuad(node); - - if (selected && !selected->wasCollected() && - (selected->getType() == Item::ITEM_BANANA || - selected->getType() == Item::ITEM_BUBBLEGUM || - selected->getType() == Item::ITEM_BUBBLEGUM_NOLOK)) + // Choose any nearby node that is in front of the AI to prevent hitting + // bad item + ArenaNode* cur_node = m_graph->getNode(*node); + const float dist = (m_kart->getXYZ() - cur_node->getCenter()).length(); + const std::vector* nearby_nodes = cur_node->getNearbyNodes(); + for (const int& nearby_node : *nearby_nodes) { - Vec3 bad_item_lc = - m_kart->getTrans().inverse()(selected->getXYZ()); - - // If satisfy the below condition, AI should not be affected by it: - // bad_item_lc.z() < 0.0f, behind the kart - if (bad_item_lc.z() < 0.0f) + Vec3 lc = m_kart->getTrans().inverse() + (m_graph->getNode(nearby_node)->getCenter()); + if (lc.z() > 0 && dist > lc.length()) { - continue; + *node = nearby_node; + return true; } - - // If the node AI will pass has a bad item, adjust the aim position - bad_item_lc = (bad_item_lc.x() < 0 ? bad_item_lc + Vec3(5, 0, 0) : - bad_item_lc - Vec3(5, 0, 0)); - m_aiming_points[1] = m_kart->getTrans()(bad_item_lc); - m_avoiding_item = true; - // Handle one banana only - return; } } - -} // updateBadItemLocation + return false; +} // checkBadItem //----------------------------------------------------------------------------- -/** This function handles braking. It used the turn radius found by - * updateTurnRadius(). Depending on the turn radius, it finds out the maximum - * speed. If the current speed is greater than the max speed and a set minimum - * speed, brakes are applied. - */ -void ArenaAI::handleArenaBraking() -{ - // A kart will not brake when the speed is already slower than this - // value. This prevents a kart from going too slow (or even backwards) - // in tight curves. - const float MIN_SPEED = 5.0f; - - if (forceBraking() && m_kart->getSpeed() > MIN_SPEED) - { - // Brake now - m_controls->m_brake = true; - return; - } - - m_controls->m_brake = false; - - if (getCurrentNode() == Graph::UNKNOWN_SECTOR || - m_target_node == Graph::UNKNOWN_SECTOR) return; - - if (m_aiming_points.empty()) return; - - const float max_turn_speed = m_kart->getSpeedForTurnRadius(m_turn_radius); - - if (m_kart->getSpeed() > 1.25f * max_turn_speed && - fabsf(m_controls->m_steer) > 0.95f && - m_kart->getSpeed() > MIN_SPEED) - { - m_controls->m_brake = true; - } - -} // handleArenaBraking - -//----------------------------------------------------------------------------- -void ArenaAI::updateTurnRadius(const Vec3& p1, const Vec3& p2, - const Vec3& p3) -{ - // First use cosine formula to find out the angle made by the distance - // between kart (point one) to point two and point two between point three - const float a = (p1 - p2).length(); - const float b = (p2 - p3).length(); - const float c = (p1 - p3).length(); - const float angle = 180 - findAngleFrom3Edges(a, b, c); - - // Only calculate radius if not almost straight line - if (angle > 1 && angle < 179) - { - // angle - // ^ - // a / \ b - // 90/\ /\90 - // \ / \ / - // \ / - // \ / - // \ / - // | - // Try to estimate the turn radius with the help of a kite-like - // polygon as shown, find out the lowest angle which is - // (4 - 2) * 180 - 90 - 90 - angle (180 - angle from above) - // Then we use this value as the angle of a sector of circle, - // a + b as the arc length, then the radius can be calculated easily - m_turn_radius = ((a + b) / (angle / 360)) / M_PI / 2; - } - else - { - // Return large radius so no braking is needed otherwise - m_turn_radius = 45.0f; - } - m_turn_angle = angle; - -} // updateTurnRadius - -//----------------------------------------------------------------------------- -float ArenaAI::findAngleFrom3Edges(float a, float b, float c) -{ - // Cosine forumla : c2 = a2 + b2 - 2ab cos C - float test_value = ((c * c) - (a * a) - (b * b)) / (-(2 * a * b)); - // Prevent error - if (test_value < -1) - test_value = -1; - else if (test_value > 1) - test_value = 1; - - return acosf(test_value) * RAD_TO_DEGREE; - -} // findAngleFrom3Edges - -//----------------------------------------------------------------------------- -void ArenaAI::handleArenaItems(const float dt) +void ArenaAI::useItems(const float dt) { m_controls->m_fire = false; if (m_kart->getKartAnimation() || @@ -675,7 +621,7 @@ void ArenaAI::handleArenaItems(const float dt) } if (m_controls->m_fire) m_time_since_last_shot = 0.0f; -} // handleArenaItems +} // useItems //----------------------------------------------------------------------------- void ArenaAI::collectItemInArena(Vec3* aim_point, int* target_node) const diff --git a/src/karts/controller/arena_ai.hpp b/src/karts/controller/arena_ai.hpp index b0e6ea7d2..6a91610b1 100644 --- a/src/karts/controller/arena_ai.hpp +++ b/src/karts/controller/arena_ai.hpp @@ -103,28 +103,22 @@ private: float m_time_since_off_road; float m_turn_radius; - float m_turn_angle; + float m_steering_angle; Vec3 m_current_forward_point; + int m_current_forward_node; - std::set m_aiming_nodes; - std::vector m_aiming_points; - - void checkIfStuck(const float dt); - void doSkiddingTest(); - float findAngleFrom3Edges(float a, float b, float c); - void handleArenaAcceleration(const float dt); - void handleArenaBraking(); - void handleArenaItems(const float dt); - void handleArenaSteering(const float dt); - void handleArenaUTurn(const float dt); - bool handleArenaUnstuck(const float dt); - bool updateAimingPosition(); - void updateBadItemLocation(); - void updateTurnRadius(const Vec3& p1, const Vec3& p2, - const Vec3& p3); + void configSpeed(); + void configSteering(); + bool checkBadItem(int* node); + void checkIfStuck(const float dt); + void doSkiddingTest(); + void doUTurn(const float dt); + bool gettingUnstuck(const float dt); + bool updateAimingPosition(Vec3* target_point); + void useItems(const float dt); virtual bool canSkid(float steer_fraction) OVERRIDE { return m_mini_skid; } virtual void findClosestKart(bool use_difficulty) = 0; diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index 6f1f3e253..db44c069c 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -2460,49 +2460,3 @@ void SkiddingAI::setSteering(float angle, float dt) } // setSteering - -// ---------------------------------------------------------------------------- -/** Determine the center point and radius of a circle given two points on - * the circle and the tangent at the first point. This is done as follows: - * 1. Determine the line going through the center point start+end, which is - * orthogonal to the vector from start to end. This line goes through the - * center of the circle. - * 2. Determine the line going through the first point and is orthogonal - * to the given tangent. - * 3. The intersection of these two lines is the center of the circle. - * \param[in] end Second point on circle. - * \param[out] center Center point of the circle (local coordinate). - * \param[out] radius Radius of the circle. - */ -void SkiddingAI::determineTurnRadius(const Vec3 &end, Vec3 *center, - float *radius) -{ - // Convert end point to local coordinate, so start will be 0, 0, 0 - Vec3 lc = m_kart->getTrans().inverse()(end); - - // 1) Line through middle of start+end - Vec3 mid = 0.5f * lc; - Vec3 direction = lc; - - Vec3 orthogonal(direction.getZ(), 0, -direction.getX()); - Vec3 q1 = mid + orthogonal; - irr::core::line2df line1(mid.getX(), mid.getZ(), - q1.getX(), q1.getZ() ); - - irr::core::line2df line2(0, 0, 1, 0); - irr::core::vector2df result; - if(line1.intersectWith(line2, result, /*checkOnlySegments*/false)) - { - Vec3 lc_center(result.X, 0, result.Y); - *center = lc_center; - *radius = lc_center.length(); - } - else - { - // No intersection. In this case assume that the two points are - // on a semicircle, in which case the center is at 0.5*(start+end): - *center = mid; - *radius = 0.5f*(lc).length(); - } - return; -} // determineTurnRadius diff --git a/src/karts/controller/skidding_ai.hpp b/src/karts/controller/skidding_ai.hpp index 72a348631..365189cd4 100644 --- a/src/karts/controller/skidding_ai.hpp +++ b/src/karts/controller/skidding_ai.hpp @@ -265,7 +265,6 @@ private: void findNonCrashingPoint(Vec3 *result, int *last_node); void determineTrackDirection(); - void determineTurnRadius(const Vec3 &end, Vec3 *center, float *radius); virtual bool canSkid(float steer_fraction); virtual void setSteering(float angle, float dt); void handleCurve(); From d3301ff006c0d3b89bdacc46f2a4290ff1a208ce Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 1 Oct 2016 12:35:57 +0800 Subject: [PATCH 251/350] Allow battle ai profiling with different track --- src/main.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 4146bb6ae..d74e5d254 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -899,13 +899,16 @@ int handleCmdLine() } if(CommandLine::has("--battle-ai-stats")) { + std::string track; + if (!CommandLine::has("--track", &track)) + track = "temple"; UserConfigParams::m_arena_ai_stats=true; race_manager->setMinorMode(RaceManager::MINOR_MODE_3_STRIKES); std::vector l; for (int i = 0; i < 8; i++) l.push_back("tux"); race_manager->setDefaultAIKartList(l); - race_manager->setTrack("temple"); + race_manager->setTrack(track); race_manager->setNumKarts(8); race_manager->setDifficulty(RaceManager::Difficulty(3)); UserConfigParams::m_no_start_screen = true; From fc94c5e4c4a7aa8d7bf4b3be28828a4a9fa83134 Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 2 Oct 2016 15:03:09 +0800 Subject: [PATCH 252/350] Change the whole path to avoid bad items --- src/karts/controller/arena_ai.cpp | 177 ++++++++++++++++++----------- src/karts/controller/arena_ai.hpp | 6 +- src/karts/controller/soccer_ai.hpp | 3 +- 3 files changed, 111 insertions(+), 75 deletions(-) diff --git a/src/karts/controller/arena_ai.cpp b/src/karts/controller/arena_ai.cpp index 1be05b4ac..dd6900e8e 100644 --- a/src/karts/controller/arena_ai.cpp +++ b/src/karts/controller/arena_ai.cpp @@ -29,6 +29,8 @@ #include "tracks/arena_graph.hpp" #include "tracks/arena_node.hpp" +#include + ArenaAI::ArenaAI(AbstractKart *kart) : AIBaseController(kart) { @@ -52,7 +54,6 @@ void ArenaAI::reset() m_closest_kart_point = Vec3(0, 0, 0); m_is_stuck = false; m_is_uturn = false; - m_avoiding_item = false; m_mini_skid = false; m_target_point = Vec3(0, 0, 0); m_target_point_lc = Vec3(0, 0, 0); @@ -79,7 +80,6 @@ void ArenaAI::update(float dt) // This is used to enable firing an item backwards. m_controls->m_look_back = false; m_controls->m_nitro = false; - m_avoiding_item = false; // Don't do anything if there is currently a kart animations shown. if (m_kart->getKartAnimation()) @@ -149,10 +149,9 @@ void ArenaAI::update(float dt) } // update //----------------------------------------------------------------------------- -/** Update aiming position, use path finding if necessary, it will set the turn - * radius too. - * \param[out] suitable target point. - * \return true if found a suitable target point. +/** Update aiming position, use path finding if necessary. + * \param[out] target_point Suitable target point. + * \return True if found a suitable target point. */ bool ArenaAI::updateAimingPosition(Vec3* target_point) { @@ -165,6 +164,7 @@ bool ArenaAI::updateAimingPosition(Vec3* target_point) m_current_forward_point = m_kart->getTrans()(Vec3(0, 0, m_kart->getKartLength())); + m_turn_radius = 0.0f; std::vector* test_nodes = NULL; if (m_current_forward_node != Graph::UNKNOWN_SECTOR) { @@ -185,35 +185,27 @@ bool ArenaAI::updateAimingPosition(Vec3* target_point) m_target_node == Graph::UNKNOWN_SECTOR) { Log::error("ArenaAI", "Next node is unknown, path finding failed!"); - m_turn_radius = 0.0f; return false; } if (forward == m_target_node) { - if (checkBadItem(&m_target_node)) - m_target_point = m_graph->getNode(m_target_node)->getCenter(); - determineTurnRadius(m_target_point, NULL, &m_turn_radius); *target_point = m_target_point; return true; } + std::vector path; int next_node = m_graph->getNextNode(forward, m_target_node); - // Use this next node for aim point if valid + if (next_node == Graph::UNKNOWN_SECTOR) { Log::error("ArenaAI", "Next node is unknown, did you forget to link" " adjacent face in navmesh?"); - m_turn_radius = 0.0f; return false; } - checkBadItem(&next_node); - *target_point = m_graph->getNode(next_node)->getCenter(); - - // Then keep getting next node to find the first turning corner which is - // used to determine turn radius + path.push_back(next_node); while (m_target_node != next_node) { int previous_node = next_node; @@ -222,31 +214,14 @@ bool ArenaAI::updateAimingPosition(Vec3* target_point) { Log::error("ArenaAI", "Next node is unknown, did you forget to" " link adjacent face in navmesh?"); - m_turn_radius = 0.0f; return false; } - - const Vec3& p1 = m_kart->getXYZ(); - const Vec3& p2 = m_graph->getNode(previous_node)->getCenter(); - const Vec3& p3 = m_graph->getNode(next_node)->getCenter(); - float edge1 = (p1 - p2).length(); - float edge2 = (p2 - p3).length(); - float to_target = (p1 - p3).length(); - - if (fabsf(edge1 + edge2 - to_target) > 0.1f) - { - // Triangle test - determineTurnRadius(p3, NULL, &m_turn_radius); -#ifdef AI_DEBUG - m_debug_sphere_next->setVisible(true); - m_debug_sphere_next->setPosition(p3.toIrrVector()); -#endif - return true; - } + path.push_back(next_node); } - // Target node == next node - determineTurnRadius(m_target_point, NULL, &m_turn_radius); + determinePath(forward, &path); + *target_point = m_graph->getNode(path.front())->getCenter(); + return true; } // updateAimingPosition @@ -397,6 +372,7 @@ void ArenaAI::doUTurn(const float dt) // End U-turn until target point is in front of this AI m_is_uturn = false; m_time_since_uturn = 0.0f; + m_time_since_driving = 0.0f; } else m_is_uturn = true; @@ -429,37 +405,6 @@ bool ArenaAI::gettingUnstuck(const float dt) } // gettingUnstuck -//----------------------------------------------------------------------------- -bool ArenaAI::checkBadItem(int* node) -{ - // Check if the node AI will cross will meet bad items - assert(*node != Graph::UNKNOWN_SECTOR); - Item* selected = ItemManager::get()->getFirstItemInQuad(*node); - - if (selected && !selected->wasCollected() && - (selected->getType() == Item::ITEM_BANANA || - selected->getType() == Item::ITEM_BUBBLEGUM || - selected->getType() == Item::ITEM_BUBBLEGUM_NOLOK)) - { - // Choose any nearby node that is in front of the AI to prevent hitting - // bad item - ArenaNode* cur_node = m_graph->getNode(*node); - const float dist = (m_kart->getXYZ() - cur_node->getCenter()).length(); - const std::vector* nearby_nodes = cur_node->getNearbyNodes(); - for (const int& nearby_node : *nearby_nodes) - { - Vec3 lc = m_kart->getTrans().inverse() - (m_graph->getNode(nearby_node)->getCenter()); - if (lc.z() > 0 && dist > lc.length()) - { - *node = nearby_node; - return true; - } - } - } - return false; -} // checkBadItem - //----------------------------------------------------------------------------- void ArenaAI::useItems(const float dt) { @@ -702,3 +647,97 @@ void ArenaAI::doSkiddingTest() } } // doSkiddingTest + +//----------------------------------------------------------------------------- +/** Determine if the path to target needs to be changed to avoid bad items, it + * will also set the turn radius based on the new path if necessary. + * \param forward Forward node of current AI position. + * \param path Default path to target. + */ +void ArenaAI::determinePath(int forward, std::vector* path) +{ + std::vector bad_item_nodes; + // First, test if the nodes AI will cross contain bad item + for (unsigned int i = 0; i < path->size(); i++) + { + // Only test few nodes ahead + if (i == 6) break; + const int node = (*path)[i]; + Item* selected = ItemManager::get()->getFirstItemInQuad(node); + + if (selected && !selected->wasCollected() && + (selected->getType() == Item::ITEM_BANANA || + selected->getType() == Item::ITEM_BUBBLEGUM || + selected->getType() == Item::ITEM_BUBBLEGUM_NOLOK)) + { + bad_item_nodes.push_back(node); + } + } + + // If so try to avoid + if (!bad_item_nodes.empty()) + { + bool failed_avoid = false; + for (unsigned int i = 0; i < path->size(); i++) + { + if (failed_avoid) break; + if (i == 6) break; + // Choose any adjacent node that is in front of the AI to prevent + // hitting bad item + ArenaNode* cur_node = + m_graph->getNode(i == 0 ? forward : (*path)[i - 1]); + float dist = 99999.9f; + const std::vector& adj_nodes = cur_node->getAdjacentNodes(); + int chosen_node = Graph::UNKNOWN_SECTOR; + for (const int& adjacent : adj_nodes) + { + if (std::find(bad_item_nodes.begin(), bad_item_nodes.end(), + adjacent) != bad_item_nodes.end()) + continue; + + Vec3 lc = m_kart->getTrans().inverse() + (m_graph->getNode(adjacent)->getCenter()); + const float dist_to_target = + m_graph->getDistance(adjacent, m_target_node); + if (lc.z() > 0 && dist > dist_to_target) + { + chosen_node = adjacent; + dist = dist_to_target; + } + if (chosen_node == Graph::UNKNOWN_SECTOR) + { + Log::debug("ArenaAI", "Too many bad items to avoid!"); + failed_avoid = true; + break; + } + (*path)[i] = chosen_node; + } + } + } + + // Now find the first turning corner to determine turn radius + for (unsigned int i = 0; i < path->size() - 1; i++) + { + const Vec3& p1 = m_kart->getXYZ(); + const Vec3& p2 = m_graph->getNode((*path)[i])->getCenter(); + const Vec3& p3 = m_graph->getNode((*path)[i + 1])->getCenter(); + float edge1 = (p1 - p2).length(); + float edge2 = (p2 - p3).length(); + float to_target = (p1 - p3).length(); + + // Triangle test + if (fabsf(edge1 + edge2 - to_target) > 0.1f) + { + determineTurnRadius(p3, NULL, &m_turn_radius); +#ifdef AI_DEBUG + m_debug_sphere_next->setVisible(true); + m_debug_sphere_next->setPosition(p3.toIrrVector()); +#endif + return; + } + } + + // Fallback calculation + determineTurnRadius(m_target_point, NULL, &m_turn_radius); + +} // determinePath diff --git a/src/karts/controller/arena_ai.hpp b/src/karts/controller/arena_ai.hpp index 6a91610b1..40adaaac5 100644 --- a/src/karts/controller/arena_ai.hpp +++ b/src/karts/controller/arena_ai.hpp @@ -63,8 +63,6 @@ protected: /** The target point. */ Vec3 m_target_point; - bool m_avoiding_item; - bool m_mini_skid; void collectItemInArena(Vec3*, int*) const; @@ -112,8 +110,8 @@ private: void configSpeed(); void configSteering(); - bool checkBadItem(int* node); void checkIfStuck(const float dt); + void determinePath(int forward, std::vector* path); void doSkiddingTest(); void doUTurn(const float dt); bool gettingUnstuck(const float dt); @@ -123,7 +121,7 @@ private: { return m_mini_skid; } virtual void findClosestKart(bool use_difficulty) = 0; virtual void findTarget() = 0; - virtual bool forceBraking() { return m_avoiding_item; } + virtual bool forceBraking() { return false; } virtual int getCurrentNode() const = 0; virtual float getKartDistance(const AbstractKart* kart) const = 0; virtual bool ignorePathFinding() { return false; } diff --git a/src/karts/controller/soccer_ai.hpp b/src/karts/controller/soccer_ai.hpp index 83bb4e21b..b9a3bcd1d 100644 --- a/src/karts/controller/soccer_ai.hpp +++ b/src/karts/controller/soccer_ai.hpp @@ -67,8 +67,7 @@ private: { return m_mini_skid && !(m_overtake_ball || m_chasing_ball); } virtual void findClosestKart(bool use_difficulty) OVERRIDE; virtual void findTarget() OVERRIDE; - virtual bool forceBraking() OVERRIDE - { return m_avoiding_item || m_force_brake; } + virtual bool forceBraking() OVERRIDE { return m_force_brake; } virtual int getCurrentNode() const OVERRIDE; virtual float getKartDistance(const AbstractKart* kart) const OVERRIDE; virtual bool ignorePathFinding() OVERRIDE From 3f71745d923e97273993a90d1e0d81df6aa0c89f Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 2 Oct 2016 15:04:08 +0800 Subject: [PATCH 253/350] Only rescue AI if it's on the ground --- src/karts/controller/arena_ai.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/karts/controller/arena_ai.cpp b/src/karts/controller/arena_ai.cpp index dd6900e8e..1fff21f5f 100644 --- a/src/karts/controller/arena_ai.cpp +++ b/src/karts/controller/arena_ai.cpp @@ -88,7 +88,7 @@ void ArenaAI::update(float dt) return; } - if (!isKartOnRoad() && !m_kart->getKartAnimation()) + if (!isKartOnRoad() && m_kart->isOnGround()) { m_time_since_off_road += dt; } @@ -98,7 +98,7 @@ void ArenaAI::update(float dt) } // If the kart needs to be rescued, do it now (and nothing else) - if (m_time_since_off_road > 5.0f && !m_kart->getKartAnimation()) + if (m_time_since_off_road > 5.0f && m_kart->isOnGround()) { m_time_since_off_road = 0.0f; new RescueAnimation(m_kart); From 6e37c5375a5f3bd0eabca713579e8e734ba7ec1d Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Sun, 2 Oct 2016 20:18:40 -0400 Subject: [PATCH 254/350] Fix end camera crash --- src/graphics/camera_end.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphics/camera_end.cpp b/src/graphics/camera_end.cpp index 6b54d193f..fbec92045 100644 --- a/src/graphics/camera_end.cpp +++ b/src/graphics/camera_end.cpp @@ -62,7 +62,7 @@ void CameraEnd::readEndCamera(const XMLNode &root) unsigned int index = i; // In reverse mode, reverse the order in which the // end cameras are read. - if(DriveGraph::get()->isReverse()) + if(DriveGraph::get() != NULL && DriveGraph::get()->isReverse()) index = root.getNumNodes() - 1 - i; const XMLNode *node = root.getNode(index); EndCameraInformation eci; From c64c8717a50a033e2d603ddacae3fead82b40bec Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 3 Oct 2016 09:28:12 +0800 Subject: [PATCH 255/350] Use 16bit integer to save some space in ArenaGraph --- src/tracks/arena_graph.cpp | 22 +++++++++++----------- src/tracks/arena_graph.hpp | 8 ++++---- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/tracks/arena_graph.cpp b/src/tracks/arena_graph.cpp index 13dbde4ea..042d4d211 100644 --- a/src/tracks/arena_graph.cpp +++ b/src/tracks/arena_graph.cpp @@ -179,8 +179,8 @@ void ArenaGraph::buildGraph() } // Allocate and initialise the previous node data structure: - m_parent_node = std::vector> - (n_nodes, std::vector(n_nodes, Graph::UNKNOWN_SECTOR)); + m_parent_node = std::vector> + (n_nodes, std::vector(n_nodes, Graph::UNKNOWN_SECTOR)); for (unsigned int i = 0; i < n_nodes; i++) { for (unsigned int j = 0; j < n_nodes; j++) @@ -347,10 +347,10 @@ void ArenaGraph::setNearbyNodesOfAllNodes() /** 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 ArenaGraph::getPathFromTo(int from, int to, - const std::vector< std::vector< int > > parent_node) +std::vector ArenaGraph::getPathFromTo(int from, int to, + const std::vector< std::vector< int16_t > >& parent_node) { - std::vector path; + std::vector path; path.push_back(to); while(from!=to) { @@ -379,7 +379,7 @@ void ArenaGraph::unitTesting() // Save the Dijkstra results std::vector< std::vector< float > > distance_matrix = ag->m_distance_matrix; - std::vector< std::vector< int > > parent_node = ag->m_parent_node; + std::vector< std::vector< int16_t > > parent_node = ag->m_parent_node; ag->buildGraph(); // Now compute results with Floyd-Warshall @@ -409,16 +409,16 @@ void ArenaGraph::unitTesting() // debugging in the feature #undef TEST_PARENT_POLY_EVEN_THOUGH_MANY_FALSE_POSITIVES #ifdef TEST_PARENT_POLY_EVEN_THOUGH_MANY_FALSE_POSITIVES - if(ag->m_parent_poly[i][j] != parent_poly[i][j]) + if(ag->m_parent_node[i][j] != parent_node[i][j]) { error_count++; - std::vector dijkstra_path = getPathFromTo(i, j, parent_node); - std::vector floyd_path = getPathFromTo(i, j, ag->m_parent_node); + std::vector dijkstra_path = getPathFromTo(i, j, parent_node); + std::vector floyd_path = getPathFromTo(i, j, ag->m_parent_node); if(dijkstra_path.size()!=floyd_path.size()) { Log::error("ArenaGraph", "Incorrect path length %d, %d: Dijkstra: %d F.W.: %d", - i, j, parent_poly[i][j], ag->m_parent_poly[i][j]); + i, j, parent_node[i][j], ag->m_parent_node[i][j]); continue; } Log::error("ArenaGraph", "Path problems from %d to %d:", @@ -431,7 +431,7 @@ void ArenaGraph::unitTesting() floyd_path[k]); } // for k> m_distance_matrix; /** The matrix that is used to store computed shortest paths. */ - std::vector> m_parent_node; + std::vector> m_parent_node; /** Used in soccer mode to colorize the goal lines in minimap. */ std::set m_red_node; @@ -58,8 +58,8 @@ private: // ------------------------------------------------------------------------ void computeFloydWarshall(); // ------------------------------------------------------------------------ - static std::vector getPathFromTo(int from, int to, - const std::vector< std::vector< int > > parent_node); + static std::vector getPathFromTo(int from, int to, + const std::vector< std::vector< int16_t > >& parent_node); // ------------------------------------------------------------------------ virtual bool hasLapLine() const OVERRIDE { return false; } // ------------------------------------------------------------------------ @@ -84,7 +84,7 @@ public: { if (i == Graph::UNKNOWN_SECTOR || j == Graph::UNKNOWN_SECTOR) return Graph::UNKNOWN_SECTOR; - return m_parent_node[j][i]; + return (int)(m_parent_node[j][i]); } // ------------------------------------------------------------------------ /** Returns the distance between any two nodes */ From e2030dabbc72a9dcd7969dc1697bfc419ad8b3ae Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 3 Oct 2016 09:54:02 +0800 Subject: [PATCH 256/350] Disable AI debug --- src/karts/controller/skidding_ai.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/karts/controller/skidding_ai.hpp b/src/karts/controller/skidding_ai.hpp index 365189cd4..68c0bc050 100644 --- a/src/karts/controller/skidding_ai.hpp +++ b/src/karts/controller/skidding_ai.hpp @@ -31,15 +31,15 @@ #ifdef DEBUG // Enable AI graphical debugging -# define AI_DEBUG +# undef AI_DEBUG // Shows left and right lines when using new findNonCrashing function -# define AI_DEBUG_NEW_FIND_NON_CRASHING +# undef AI_DEBUG_NEW_FIND_NON_CRASHING // Show the predicted turn circles -# define AI_DEBUG_CIRCLES +# undef AI_DEBUG_CIRCLES // Show the heading of the kart -# define AI_DEBUG_KART_HEADING +# undef AI_DEBUG_KART_HEADING // Shows line from kart to its aim point -# define AI_DEBUG_KART_AIM +# undef AI_DEBUG_KART_AIM #endif From 13eaf82af1a74a87019407ca764f600ceacd4bba Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 3 Oct 2016 10:02:27 +0800 Subject: [PATCH 257/350] Patch TestAI with changes to SkiddingAI --- src/karts/controller/test_ai.cpp | 175 ++++++++----------------------- src/karts/controller/test_ai.hpp | 5 - 2 files changed, 45 insertions(+), 135 deletions(-) diff --git a/src/karts/controller/test_ai.cpp b/src/karts/controller/test_ai.cpp index 1a57101fa..cdc35616a 100644 --- a/src/karts/controller/test_ai.cpp +++ b/src/karts/controller/test_ai.cpp @@ -179,6 +179,7 @@ void SkiddingAI::reset() m_curve_center = Vec3(0,0,0); m_current_track_direction = DriveNode::DIR_STRAIGHT; m_item_to_collect = NULL; + m_last_direction_node = 0; m_avoid_item_close = false; m_skid_probability_state = SKID_PROBAB_NOT_YET; m_last_item_random = NULL; @@ -785,14 +786,8 @@ void SkiddingAI::handleItemCollectionAndAvoidance(Vec3 *aim_point, { // Kart will not hit item, try to get closer to this item // so that it can potentially become a permanent target. - Vec3 xyz = item_to_collect->getXYZ(); - Vec3 item_direction = xyz - m_kart->getXYZ(); - Vec3 plane_normal = DriveGraph::get()->getNode(m_track_node) - ->getNormal(); - float dist_to_plane = item_direction.dot(plane_normal); - Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; - - float angle_to_item = (projected_xyz - m_kart->getXYZ()) + const Vec3& xyz = item_to_collect->getXYZ(); + float angle_to_item = (xyz - m_kart->getXYZ()) .angle(kart_aim_direction); float angle = normalizeAngle(angle_to_item); @@ -865,19 +860,8 @@ bool SkiddingAI::handleSelectedItem(Vec3 kart_aim_direction, Vec3 *aim_point) if(m_item_to_collect->getDisableTime()>0) return false; - // Project the item's location onto the plane of the current quad. - // This is necessary because the kart's aim point may not be on the track - // in 3D curves. So we project the item's location onto the plane in which - // the kart is. The current quad provides a good estimate of the kart's plane. const Vec3 &xyz = m_item_to_collect->getXYZ(); - Vec3 item_direction = xyz - m_kart->getXYZ(); - Vec3 plane_normal = DriveGraph::get()->getNode(m_track_node)->getNormal(); - float dist_to_plane = item_direction.dot(plane_normal); - Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; - - float angle_to_item = (projected_xyz - m_kart->getXYZ()) - .angle(kart_aim_direction); - + float angle_to_item = (xyz - m_kart->getXYZ()).angle(kart_aim_direction); float angle = normalizeAngle(angle_to_item); if(fabsf(angle)>1.5) @@ -930,9 +914,9 @@ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, // Check if we would drive left of the leftmost or right of the // rightmost point - if so, nothing to do. - Vec3 left(items_to_avoid[index_left_most]->getXYZ()); + const Vec3& left = items_to_avoid[index_left_most]->getXYZ(); int node_index = items_to_avoid[index_left_most]->getGraphNode(); - Vec3 normal = DriveGraph::get()->getNode(node_index)->getNormal(); + const Vec3& normal = DriveGraph::get()->getNode(node_index)->getNormal(); Vec3 p1 = line_to_target.start, p2 = line_to_target.getMiddle() + normal.toIrrVector(), p3 = line_to_target.end; @@ -950,14 +934,14 @@ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, } else { - Vec3 left(items_to_avoid[index_left_most]->getXYZ()); - int node_index = items_to_avoid[index_left_most]->getGraphNode(); - Vec3 normal = DriveGraph::get()->getNode(node_index)->getNormal(); + const Vec3& right = items_to_avoid[index_right_most]->getXYZ(); + int node_index = items_to_avoid[index_right_most]->getGraphNode(); + const Vec3& normal = DriveGraph::get()->getNode(node_index)->getNormal(); Vec3 p1 = line_to_target.start, p2 = line_to_target.getMiddle() + normal.toIrrVector(), p3 = line_to_target.end; - if (left.sideofPlane(p1, p2, p3) >= 0) + if (right.sideofPlane(p1, p2, p3) >= 0) { // Right of rightmost point item_index = index_right_most; @@ -1002,7 +986,6 @@ bool SkiddingAI::steerToAvoid(const std::vector &items_to_avoid, for(unsigned int i=0; igetXYZ(); - core::vector2df item2d = xyz.toIrrVector2d(); core::vector3df point3d = line_to_target.getClosestPoint(xyz.toIrrVector()); float d = (xyz.toIrrVector() - point3d).getLengthSQ(); float direction = xyz.sideofPlane(p1,p2,p3); @@ -1104,19 +1087,9 @@ void SkiddingAI::evaluateItems(const Item *item, Vec3 kart_aim_direction, // to avoid are collected). if(!avoid) { - // Project the item's location onto the plane of the current quad. - // This is necessary because the kart's aim point may not be on the track - // in 3D curves. So we project the item's location onto the plane in which - // the kart is. The current quad provides a good estimate of the kart's plane. const Vec3 &xyz = item->getXYZ(); - Vec3 item_direction = xyz - m_kart->getXYZ(); - Vec3 plane_normal = DriveGraph::get()->getNode(m_track_node)->getNormal(); - float dist_to_plane = item_direction.dot(plane_normal); - Vec3 projected_xyz = xyz - dist_to_plane*plane_normal; - - float angle_to_item = (projected_xyz - m_kart->getXYZ()) - .angle(kart_aim_direction); - + float angle_to_item = + (xyz - m_kart->getXYZ()).angle(kart_aim_direction); float diff = normalizeAngle(angle_to_item); // The kart is driving at high speed, when the current max speed @@ -1814,16 +1787,14 @@ void SkiddingAI::checkCrashes(const Vec3& pos ) const size_t NUM_KARTS = m_world->getNumKarts(); - //Protection against having vel_normal with nan values - const Vec3 &VEL = m_kart->getVelocity(); - Vec3 vel_normal(VEL.getX(), 0.0, VEL.getZ()); - float speed=vel_normal.length(); + float speed = m_kart->getVelocity().length(); // If the velocity is zero, no sense in checking for crashes in time if(speed==0) return; + Vec3 vel_normal = m_kart->getVelocity().normalized(); + // Time it takes to drive for m_kart_length units. float dt = m_kart_length / speed; - vel_normal/=speed; int current_node = m_track_node; if(steps<1 || steps>1000) @@ -1853,7 +1824,7 @@ void SkiddingAI::checkCrashes(const Vec3& pos ) continue; Vec3 other_kart_xyz = other_kart->getXYZ() + other_kart->getVelocity()*(i*dt); - float kart_distance = (step_coord - other_kart_xyz).length_2d(); + float kart_distance = (step_coord - other_kart_xyz).length(); if( kart_distance < m_kart_length) m_crashes.m_kart = j; @@ -2185,20 +2156,17 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) */ void SkiddingAI::determineTrackDirection() { - const DriveGraph *qg = DriveGraph::get(); - unsigned int succ = m_successor_index[m_track_node]; - unsigned int next = qg->getNode(m_track_node)->getSuccessor(succ); - - //float angle_to_track = qg->getNode(m_track_node)->getAngleToSuccessor(succ) - // - m_kart->getHeading(); - Vec3 track_direction = -qg->getNode(m_track_node)->getCenter() - + qg->getNode(next)->getCenter(); - //Vec3 kart_direction = qg->getNode(m_track_node)->getCenter() + m_kart->getVelocity(); - - float angle_to_track = 0; + const DriveGraph *dg = DriveGraph::get(); + unsigned int succ = m_successor_index[m_track_node]; + unsigned int next = dg->getNode(m_track_node)->getSuccessor(succ); + float angle_to_track = 0.0f; if (m_kart->getVelocity().length() > 0.0f) - angle_to_track = track_direction.angle(m_kart->getVelocity().normalized()); - + { + Vec3 track_direction = -dg->getNode(m_track_node)->getCenter() + + dg->getNode(next)->getCenter(); + angle_to_track = + track_direction.angle(m_kart->getVelocity().normalized()); + } angle_to_track = normalizeAngle(angle_to_track); // In certain circumstances (esp. S curves) it is possible that the @@ -2218,15 +2186,15 @@ void SkiddingAI::determineTrackDirection() return; } - qg->getNode(next)->getDirectionData(m_successor_index[next], - &m_current_track_direction, - &m_last_direction_node); + dg->getNode(next)->getDirectionData(m_successor_index[next], + &m_current_track_direction, + &m_last_direction_node); #ifdef AI_DEBUG m_curve[CURVE_QG]->clear(); for(unsigned int i=m_track_node; i<=m_last_direction_node; i++) { - m_curve[CURVE_QG]->addPoint(qg->getNode(i)->getCenter()); + m_curve[CURVE_QG]->addPoint(dg->getNode(i)->getCenter()); } #endif @@ -2256,13 +2224,10 @@ void SkiddingAI::handleCurve() // kart will already point towards the direction of the circle), and // the case that the kart is facing wrong was already tested for before - const DriveGraph *qg = DriveGraph::get(); - Vec3 xyz = m_kart->getXYZ(); - Vec3 tangent = m_kart->getTrans()(Vec3(0,0,1)) - xyz; - Vec3 last_xyz = qg->getNode(m_last_direction_node)->getCenter(); + const DriveGraph *dg = DriveGraph::get(); + const Vec3& last_xyz = dg->getNode(m_last_direction_node)->getCenter(); - determineTurnRadius(xyz, tangent, last_xyz, - &m_curve_center, &m_current_curve_radius); + determineTurnRadius(last_xyz, &m_curve_center, &m_current_curve_radius); assert(!std::isnan(m_curve_center.getX())); assert(!std::isnan(m_curve_center.getY())); assert(!std::isnan(m_curve_center.getZ())); @@ -2278,11 +2243,12 @@ void SkiddingAI::handleCurve() // Pick either the lower left or right point: int index = m_current_track_direction==DriveNode::DIR_LEFT ? 0 : 1; - float r = (m_curve_center - *(qg->getNode(i))[index]).length(); + Vec3 curve_center_wc = m_kart->getTrans()(m_curve_center); + float r = (curve_center_wc - *(dg->getNode(i))[index]).length(); if(m_current_curve_radius < r) { - last_xyz = *(qg->getNode(i))[index]; - determineTurnRadius(xyz, tangent, last_xyz, + last_xyz = *(dg->getNode(i)[index]); + determineTurnRadius(last_xyz, &m_curve_center, &m_current_curve_radius); m_last_direction_node = i; break; @@ -2292,11 +2258,11 @@ void SkiddingAI::handleCurve() } #endif #if defined(AI_DEBUG) && defined(AI_DEBUG_CIRCLES) - m_curve[CURVE_PREDICT1]->makeCircle(m_curve_center, + m_curve[CURVE_PREDICT1]->makeCircle(m_kart->getTrans()(m_curve_center), m_current_curve_radius); m_curve[CURVE_PREDICT1]->addPoint(last_xyz); - m_curve[CURVE_PREDICT1]->addPoint(m_curve_center); - m_curve[CURVE_PREDICT1]->addPoint(xyz); + m_curve[CURVE_PREDICT1]->addPoint(m_kart->getTrans()(m_curve_center)); + m_curve[CURVE_PREDICT1]->addPoint(m_kart->getXYZ()); #endif } // handleCurve @@ -2350,15 +2316,16 @@ bool SkiddingAI::canSkid(float steer_fraction) } const float MIN_SKID_SPEED = 5.0f; - const DriveGraph *qg = DriveGraph::get(); - Vec3 last_xyz = qg->getNode(m_last_direction_node)->getCenter(); + const DriveGraph *dg = DriveGraph::get(); + Vec3 last_xyz = m_kart->getTrans().inverse() + (dg->getNode(m_last_direction_node)->getCenter()); // Only try skidding when a certain minimum speed is reached. if(m_kart->getSpeed()getXYZ() - m_curve_center; - Vec3 diff_last = last_xyz - m_curve_center; + Vec3 diff_kart = -m_curve_center; + Vec3 diff_last = last_xyz - m_curve_center; float angle_kart = atan2(diff_kart.getX(), diff_kart.getZ()); float angle_last = atan2(diff_last.getX(), diff_last.getZ()); float angle = m_current_track_direction == DriveNode::DIR_RIGHT @@ -2542,55 +2509,3 @@ void SkiddingAI::setSteering(float angle, float dt) } // setSteering - -// ---------------------------------------------------------------------------- -/** Determine the center point and radius of a circle given two points on - * the ccircle and the tangent at the first point. This is done as follows: - * 1. Determine the line going through the center point start+end, which is - * orthogonal to the vector from start to end. This line goes through the - * center of the circle. - * 2. Determine the line going through the first point and is orthogonal - * to the given tangent. - * 3. The intersection of these two lines is the center of the circle. - * \param[in] start First point. - * \param[in] tangent Tangent at first point. - * \param[in] end Second point on circle. - * \param[out] center Center point of the circle. - * \param[out] radius Radius of the circle. - */ -void SkiddingAI::determineTurnRadius(const Vec3 &start, - const Vec3 &tangent, - const Vec3 &end, - Vec3 *center, - float *radius) -{ - // 1) Line through middle of start+end - Vec3 mid = 0.5f*(start+end); - Vec3 direction = end-start; - - Vec3 orthogonal(direction.getZ(), 0, -direction.getX()); - Vec3 q1 = mid + orthogonal; - irr::core::line2df line1(mid.getX(), mid.getZ(), - q1.getX(), q1.getZ() ); - - Vec3 ortho_tangent(tangent.getZ(), 0, -tangent.getX()); - Vec3 q2 = start + ortho_tangent; - irr::core::line2df line2(start.getX(), start.getZ(), - q2.getX(), q2.getZ()); - - - irr::core::vector2df result; - if(line1.intersectWith(line2, result, /*checkOnlySegments*/false)) - { - *center = Vec3(result.X, start.getY(), result.Y); - *radius = (start - *center).length(); - } - else - { - // No intersection. In this case assume that the two points are - // on a semicircle, in which case the center is at 0.5*(start+end): - *center = 0.5f*(start+end); - *radius = 0.5f*(end-start).length(); - } - return; -} // determineTurnRadius diff --git a/src/karts/controller/test_ai.hpp b/src/karts/controller/test_ai.hpp index e1f968c48..97f6d90da 100644 --- a/src/karts/controller/test_ai.hpp +++ b/src/karts/controller/test_ai.hpp @@ -221,11 +221,6 @@ private: void findNonCrashingPoint(Vec3 *result, int *last_node); void determineTrackDirection(); - void determineTurnRadius(const Vec3 &start, - const Vec3 &start_direction, - const Vec3 &end, - Vec3 *center, - float *radius); virtual bool canSkid(float steer_fraction); virtual void setSteering(float angle, float dt); void handleCurve(); From 21a04213d588990b614a38701e10bff4649145ac Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 3 Oct 2016 10:30:06 +0800 Subject: [PATCH 258/350] Fix typo --- src/tracks/graph.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tracks/graph.hpp b/src/tracks/graph.hpp index 56297882b..8f713cb6e 100644 --- a/src/tracks/graph.hpp +++ b/src/tracks/graph.hpp @@ -106,7 +106,7 @@ public: { assert(m_graph == NULL); m_graph = graph; - } // create + } // setGraph // ------------------------------------------------------------------------ /** Cleans up the graph. It is possible that this function is called even * if no instance exists (e.g. arena without navmesh). So it is not an From 50b1a8cdfb26aa38726be2df1daa2e74df3dc00d Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 3 Oct 2016 18:13:03 +1100 Subject: [PATCH 259/350] Added some (commented out) code to investigate camera shaking - if it should happen again. --- src/karts/kart.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index ee4f1e021..2bc0278c6 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1214,6 +1214,18 @@ void Kart::update(float dt) if(!history->replayHistory() && !RewindManager::get()->isRewinding()) m_controller->update(dt); +#undef DEBUG_CAMERA_SHAKE +#ifdef DEBUG_CAMERA_SHAKE + Log::verbose("camera", "%s t %f %f xyz %f %f %f v %f %f %f d3 %f d2 %f", + getIdent().c_str(), + World::getWorld()->getTime(), dt, + getXYZ().getX(), getXYZ().getY(), getXYZ().getZ(), + getVelocity().getX(), getVelocity().getY(), getVelocity().getZ(), + (Camera::getCamera(0)->getXYZ()-getXYZ()).length(), + (Camera::getCamera(0)->getXYZ()-getXYZ()).length_2d() + ); +#endif + #undef DEBUG_TO_COMPARE_KART_PHYSICS #ifdef DEBUG_TO_COMPARE_KART_PHYSICS // This information is useful when comparing kart physics, e.g. to From 84a3f5d8b27a8e472d02c9dd209755c5beae6f87 Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 4 Oct 2016 08:16:35 +1100 Subject: [PATCH 260/350] Fixed compiler warnings. --- src/graphics/irr_driver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 048f7b88c..ae13f8d52 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -219,7 +219,7 @@ void IrrDriver::computeMatrixesAndCameras(scene::ICameraSceneNode *const camnode float w = width * UserConfigParams::m_scale_rtts_factor; float h = height * UserConfigParams::m_scale_rtts_factor; m_current_screen_size = core::vector2df(w, h); - m_shadow_matrices->computeMatrixesAndCameras(camnode, w, h); + m_shadow_matrices->computeMatrixesAndCameras(camnode, int(w), int(h)); } // computeMatrixesAndCameras // ---------------------------------------------------------------------------- From 5e05f1178ce6bc5f3a653b55ab3dc6d016196341 Mon Sep 17 00:00:00 2001 From: Deve Date: Mon, 3 Oct 2016 23:26:09 +0200 Subject: [PATCH 261/350] Fixed compiler error on Linux with non-x86 64bit platforms, e.g. arm64, mips, and s390x architectures This modification is already applied in upstream angelscript repository: https://sourceforge.net/p/angelscript/code/2353/ Thanks to Adrian Bunk and Andreas Jonsson --- lib/angelscript/projects/cmake/CMakeLists.txt | 1 + lib/angelscript/source/as_config.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/angelscript/projects/cmake/CMakeLists.txt b/lib/angelscript/projects/cmake/CMakeLists.txt index e93971315..755d8378c 100644 --- a/lib/angelscript/projects/cmake/CMakeLists.txt +++ b/lib/angelscript/projects/cmake/CMakeLists.txt @@ -67,6 +67,7 @@ set(ANGELSCRIPT_SOURCE ../../source/as_builder.cpp ../../source/as_bytecode.cpp ../../source/as_callfunc.cpp + ../../source/as_callfunc_mips.cpp ../../source/as_callfunc_x86.cpp ../../source/as_callfunc_x64_gcc.cpp ../../source/as_callfunc_x64_msvc.cpp diff --git a/lib/angelscript/source/as_config.h b/lib/angelscript/source/as_config.h index cb05bffbd..5bb5b8e80 100644 --- a/lib/angelscript/source/as_config.h +++ b/lib/angelscript/source/as_config.h @@ -844,7 +844,7 @@ #define THISCALL_PASS_OBJECT_POINTER_ON_THE_STACK #define AS_X86 #undef AS_NO_THISCALL_FUNCTOR_METHOD - #elif defined(__LP64__) && !defined(__arm64__) + #elif defined(__x86_64__) #define AS_X64_GCC #undef AS_NO_THISCALL_FUNCTOR_METHOD #define HAS_128_BIT_PRIMITIVES From 9595a76d36c757c832487d65110381407aebc9e4 Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 4 Oct 2016 08:28:01 +1100 Subject: [PATCH 262/350] Removed unnecessary flag to keep track if speed icnrease is defined. --- src/physics/btKart.cpp | 5 +---- src/physics/btKart.hpp | 3 --- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/physics/btKart.cpp b/src/physics/btKart.cpp index 446c1daab..05e216467 100644 --- a/src/physics/btKart.cpp +++ b/src/physics/btKart.cpp @@ -116,7 +116,6 @@ void btKart::reset() updateWheelTransform(i, true); } m_visual_wheels_touch_ground = false; - m_zipper_active = false; m_zipper_speed = btScalar(0); m_skid_angular_velocity = 0; m_is_skidding = false; @@ -810,7 +809,7 @@ void btKart::updateFriction(btScalar timeStep) (btRigidBody*) wheelInfo.m_raycastInfo.m_groundObject; if(!groundObject) continue; - if(m_zipper_active && m_zipper_speed > 0) + if(m_zipper_speed > 0) { if (wheel==2 || wheel==3) { @@ -882,7 +881,6 @@ void btKart::updateFriction(btScalar timeStep) } // for (int wheel=0; wheel 0; m_zipper_speed = speed; } // activateZipper diff --git a/src/physics/btKart.hpp b/src/physics/btKart.hpp index b81c6ed44..38b6850bc 100644 --- a/src/physics/btKart.hpp +++ b/src/physics/btKart.hpp @@ -69,9 +69,6 @@ private: btScalar m_damping; btVehicleRaycaster *m_vehicleRaycaster; - /** True if a zipper is active for that kart. */ - bool m_zipper_active; - /** The zipper speed (i.e. the velocity the kart should reach in * the first frame that the zipper is active). */ btScalar m_zipper_speed; From 4c0b4d80780cb35f4e36fbf8f96970c3723b0fae Mon Sep 17 00:00:00 2001 From: Nado Date: Wed, 24 Aug 2016 16:45:27 +0200 Subject: [PATCH 263/350] Fixes `-Wsign-compare` See #2613 --- src/graphics/irr_driver.cpp | 2 +- src/states_screens/credits.cpp | 2 +- src/tracks/check_line.cpp | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 516613380..9cf83af79 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -1600,7 +1600,7 @@ std::string IrrDriver::getSmallerTexture(const std::string& filename) { core::dimension2d dim = img->getDimension(); core::dimension2d new_dim; // Dimension of the cached texture - const int scale_factor = 2; + const unsigned scale_factor = 2; // Resize the texture only if it can be done properly if (dim.Width < scale_factor || dim.Height < scale_factor) new_dim = dim; diff --git a/src/states_screens/credits.cpp b/src/states_screens/credits.cpp index e7a6a5b24..39083cf2a 100644 --- a/src/states_screens/credits.cpp +++ b/src/states_screens/credits.cpp @@ -215,7 +215,7 @@ void CreditsScreen::loadedFromFile() irr::core::stringw translators_credits = _("translator-credits"); - const int MAX_PER_SCREEN = 6; + const unsigned MAX_PER_SCREEN = 6; if (translators_credits != L"translator-credits") { diff --git a/src/tracks/check_line.cpp b/src/tracks/check_line.cpp index eaed7c20d..e9374ca93 100644 --- a/src/tracks/check_line.cpp +++ b/src/tracks/check_line.cpp @@ -30,6 +30,7 @@ #include "irrlicht.h" #include +#include #include /** Constructor for a checkline. @@ -171,7 +172,7 @@ bool CheckLine::isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, bool previous_sign; - if (kart_index == -1) + if (kart_index == UINT_MAX) { core::vector2df p = old_pos.toIrrVector2d(); previous_sign = (m_line.getPointOrientation(p) >= 0); @@ -212,10 +213,10 @@ bool CheckLine::isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, else result = false; - if (kart_index != -1) + if (kart_index != UINT_MAX) m_previous_sign[kart_index] = sign; - if (result && kart_index != -1) + if (result && kart_index != UINT_MAX) { LinearWorld* lw = dynamic_cast(w); if (lw != NULL) From afbea19d346492282962c04e0ab30dc12beade16 Mon Sep 17 00:00:00 2001 From: Nado Date: Fri, 26 Aug 2016 18:49:34 +0200 Subject: [PATCH 264/350] Fix -Wunused-variable --- src/scriptengine/script_engine.cpp | 3 +-- src/states_screens/server_selection.cpp | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/scriptengine/script_engine.cpp b/src/scriptengine/script_engine.cpp index 7bd4e7bd1..f946d131b 100644 --- a/src/scriptengine/script_engine.cpp +++ b/src/scriptengine/script_engine.cpp @@ -378,7 +378,7 @@ namespace Scripting Log::error("Scripting", "The script ended with an exception."); // Write some information about the script exception - asIScriptFunction *func = ctx->GetExceptionFunction(); + //asIScriptFunction *func = ctx->GetExceptionFunction(); //std::cout << "func: " << func->GetDeclaration() << std::endl; //std::cout << "modl: " << func->GetModuleName() << std::endl; //std::cout << "sect: " << func->GetScriptSectionName() << std::endl; @@ -520,7 +520,6 @@ namespace Scripting { if (m_callback_delegate != NULL) { - asIScriptEngine* engine = World::getWorld()->getScriptEngine()->getEngine(); m_callback_delegate->Release(); } } diff --git a/src/states_screens/server_selection.cpp b/src/states_screens/server_selection.cpp index b34869433..04101bf8e 100644 --- a/src/states_screens/server_selection.cpp +++ b/src/states_screens/server_selection.cpp @@ -241,7 +241,6 @@ void ServerSelection::onUpdate(float dt) m_server_list_widget->clear(); - ServersManager *manager = ServersManager::get(); loadList(); m_server_list_widget->addItem("spacer", L""); m_server_list_widget->addItem("loading", From 25a4aa565a15948b987b45d7b80eac824a792762 Mon Sep 17 00:00:00 2001 From: Nado Date: Sat, 27 Aug 2016 22:45:04 +0200 Subject: [PATCH 265/350] Reduce variables scope when possible, based on the warnings reported by cppcheck. --- src/audio/music_ogg.cpp | 3 +- src/challenges/challenge_data.cpp | 1 - src/graphics/render_geometry.cpp | 4 +-- src/graphics/shadow_matrices.cpp | 29 ++++++++++--------- src/graphics/stk_scene_manager.cpp | 4 ++- src/guiengine/event_handler.cpp | 4 +-- src/guiengine/skin.cpp | 2 +- src/guiengine/widgets/CGUIEditBox.cpp | 3 +- .../widgets/dynamic_ribbon_widget.cpp | 2 +- src/input/wiimote_manager.cpp | 1 - src/karts/controller/end_controller.cpp | 6 ++-- src/karts/controller/skidding_ai.cpp | 9 ++---- src/karts/controller/test_ai.cpp | 10 ++----- src/karts/kart_model.cpp | 3 +- src/karts/skidding.cpp | 8 ++--- src/main.cpp | 1 - src/online/online_player_profile.cpp | 3 +- src/scriptengine/scriptarray.cpp | 4 +-- src/states_screens/edit_track_screen.cpp | 4 +-- src/states_screens/kart_selection.cpp | 3 +- src/states_screens/options_screen_device.cpp | 3 +- 21 files changed, 42 insertions(+), 65 deletions(-) diff --git a/src/audio/music_ogg.cpp b/src/audio/music_ogg.cpp index df7613f3d..132aad01f 100644 --- a/src/audio/music_ogg.cpp +++ b/src/audio/music_ogg.cpp @@ -333,11 +333,10 @@ bool MusicOggStream::streamIntoBuffer(ALuint buffer) int size = 0; int portion; - int result; while(size < m_buffer_size) { - result = ov_read(&m_oggStream, pcm + size, m_buffer_size - size, + int result = ov_read(&m_oggStream, pcm + size, m_buffer_size - size, isBigEndian, 2, 1, &portion); if(result > 0) diff --git a/src/challenges/challenge_data.cpp b/src/challenges/challenge_data.cpp index f1fdc2f27..e6b880fd8 100644 --- a/src/challenges/challenge_data.cpp +++ b/src/challenges/challenge_data.cpp @@ -516,7 +516,6 @@ const irr::core::stringw if (track == NULL) return irr::core::stringw( L"????" ); return _("New track '%s' now available", track->getName()); - break; } case UNLOCK_MODE: { diff --git a/src/graphics/render_geometry.cpp b/src/graphics/render_geometry.cpp index 522efafbc..9b642b936 100644 --- a/src/graphics/render_geometry.cpp +++ b/src/graphics/render_geometry.cpp @@ -1319,9 +1319,9 @@ void multidraw2ndPass(const std::vector &Handles, Args... args) T::InstancedSecondPassShader::getInstance()->use(); glBindVertexArray(VAOManager::getInstance()->getInstanceVAO(T::VertexType, T::Instance)); - uint64_t nulltex[10] = {}; if (SolidPassCmd::getInstance()->Size[T::MaterialType]) { + uint64_t nulltex[10] = {}; HandleExpander::template Expand(nulltex, T::SecondPassTextures, Handles[0], Handles[1], Handles[2]); @@ -1410,9 +1410,9 @@ void IrrDriver::renderSolidSecondPass() GrassMat::InstancedSecondPassShader::getInstance()->use(); glBindVertexArray(VAOManager::getInstance()->getInstanceVAO(GrassMat::VertexType, GrassMat::Instance)); - uint64_t nulltex[10] = {}; if (SolidPassCmd::getInstance()->Size[GrassMat::MaterialType]) { + uint64_t nulltex[10] = {}; HandleExpander ::Expand(nulltex, GrassMat::SecondPassTextures, DiffuseHandle, SpecularHandle, SSAOHandle, DepthHandle); diff --git a/src/graphics/shadow_matrices.cpp b/src/graphics/shadow_matrices.cpp index dab159677..260cf28ba 100644 --- a/src/graphics/shadow_matrices.cpp +++ b/src/graphics/shadow_matrices.cpp @@ -314,20 +314,6 @@ void ShadowMatrices::computeMatrixesAndCameras(scene::ICameraSceneNode *const ca const float oldfar = camnode->getFarValue(); const float oldnear = camnode->getNearValue(); - float FarValues[] = - { - ShadowMatrices::m_shadow_split[1], - ShadowMatrices::m_shadow_split[2], - ShadowMatrices::m_shadow_split[3], - ShadowMatrices::m_shadow_split[4], - }; - float NearValues[] = - { - ShadowMatrices::m_shadow_split[0], - ShadowMatrices::m_shadow_split[1], - ShadowMatrices::m_shadow_split[2], - ShadowMatrices::m_shadow_split[3] - }; float tmp[16 * 9 + 2]; memcpy(tmp, irr_driver->getViewMatrix().pointer(), 16 * sizeof(float)); @@ -359,6 +345,21 @@ void ShadowMatrices::computeMatrixesAndCameras(scene::ICameraSceneNode *const ca core::aabbox3df trackbox(vmin.toIrrVector(), vmax.toIrrVector() - core::vector3df(0, 30, 0)); + float FarValues[] = + { + ShadowMatrices::m_shadow_split[1], + ShadowMatrices::m_shadow_split[2], + ShadowMatrices::m_shadow_split[3], + ShadowMatrices::m_shadow_split[4], + }; + float NearValues[] = + { + ShadowMatrices::m_shadow_split[0], + ShadowMatrices::m_shadow_split[1], + ShadowMatrices::m_shadow_split[2], + ShadowMatrices::m_shadow_split[3] + }; + // Shadow Matrixes and cameras for (unsigned i = 0; i < 4; i++) { diff --git a/src/graphics/stk_scene_manager.cpp b/src/graphics/stk_scene_manager.cpp index 12b532b2c..898539c9a 100644 --- a/src/graphics/stk_scene_manager.cpp +++ b/src/graphics/stk_scene_manager.cpp @@ -723,7 +723,7 @@ PROFILER_POP_CPU_MARKER(); ListInstancedMatDetails::getInstance()->clear(); ListInstancedMatUnlit::getInstance()->clear(); - size_t SolidPoly = 0, ShadowPoly = 0, MiscPoly = 0; + size_t SolidPoly = 0, ShadowPoly = 0; PROFILER_PUSH_CPU_MARKER("- Draw Command upload", 0xFF, 0x0, 0xFF); @@ -868,6 +868,8 @@ PROFILER_POP_CPU_MARKER(); RSMCmdBuffer = (DrawElementsIndirectCommand*)glMapBufferRange(GL_DRAW_INDIRECT_BUFFER, 0, 10000 * sizeof(DrawElementsIndirectCommand), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); } + size_t MiscPoly = 0; + // Default Material RSMPassCmd::getInstance()->Offset[Material::SHADERTYPE_SOLID] = current_cmd; FillInstances(MeshForRSM[Material::SHADERTYPE_SOLID], ListInstancedMatDefault::getInstance()->RSM, RSMInstanceBuffer, RSMCmdBuffer, offset, current_cmd, MiscPoly); diff --git a/src/guiengine/event_handler.cpp b/src/guiengine/event_handler.cpp index 2dc5357cd..802857d8f 100644 --- a/src/guiengine/event_handler.cpp +++ b/src/guiengine/event_handler.cpp @@ -387,7 +387,7 @@ const bool NAVIGATION_DEBUG = false; */ void EventHandler::navigate(const int playerID, Input::InputType type, const bool pressedDown, const bool reverse) { - IGUIElement *el = NULL, *closest = NULL; + IGUIElement *el = NULL; if (type == Input::IT_STICKBUTTON && !pressedDown) return; @@ -450,7 +450,7 @@ void EventHandler::navigate(const int playerID, Input::InputType type, const boo // Down: if the current widget is e.g. 5, search for widget 6, 7, 8, 9, ..., 15 (up to 10 IDs may be missing) for (int n = 1; n < 10 && !found; n++) { - closest = GUIEngine::getGUIEnv()->getRootGUIElement()->getElementFromId(el->getTabOrder() + (reverse ? -n : n), true); + IGUIElement *closest = GUIEngine::getGUIEnv()->getRootGUIElement()->getElementFromId(el->getTabOrder() + (reverse ? -n : n), true); if (closest != NULL && Widget::isFocusableId(closest->getID())) { diff --git a/src/guiengine/skin.cpp b/src/guiengine/skin.cpp index 818671d2e..b1588ccb9 100644 --- a/src/guiengine/skin.cpp +++ b/src/guiengine/skin.cpp @@ -927,7 +927,6 @@ void Skin::drawRibbonChild(const core::recti &rect, Widget* widget, //if (widget->m_deactivated) return; bool mark_selected = widget->isSelected(PLAYER_ID_GAME_MASTER); - bool always_show_selection = false; IGUIElement* focusedElem = NULL; if (GUIEngine::getFocusForPlayer(PLAYER_ID_GAME_MASTER) != NULL) @@ -1007,6 +1006,7 @@ void Skin::drawRibbonChild(const core::recti &rect, Widget* widget, /* in combo ribbons, always show selection */ RibbonWidget* parentRibbonWidget = NULL; + bool always_show_selection = false; if (widget->m_event_handler != NULL && widget->m_event_handler->m_type == WTYPE_RIBBON) diff --git a/src/guiengine/widgets/CGUIEditBox.cpp b/src/guiengine/widgets/CGUIEditBox.cpp index c0755d866..cf3b9d7a2 100644 --- a/src/guiengine/widgets/CGUIEditBox.cpp +++ b/src/guiengine/widgets/CGUIEditBox.cpp @@ -1321,11 +1321,10 @@ void CGUIEditBox::breakText() s32 size = Text.size(); s32 length = 0; s32 elWidth = RelativeRect.getWidth() - 6; - wchar_t c; for (s32 i=0; igetNode(target_sector)->getCenter() - m_kart->getXYZ(); float len=direction.length(); - steps = int( len / m_kart_length ); + int steps = int( len / m_kart_length ); if( steps < 3 ) steps = 3; //Protection against having vel_normal with nan values diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index db44c069c..7b4549b32 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -1934,7 +1934,6 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) m_curve[CURVE_KART]->addPoint(m_kart->getTrans()(forw)+eps); #endif *last_node = m_next_node_index[m_track_node]; - int target_sector; Vec3 direction; Vec3 step_track_coord; @@ -1946,7 +1945,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) { // target_sector is the sector at the longest distance that we can // drive to without crashing with the track. - target_sector = m_next_node_index[*last_node]; + int target_sector = m_next_node_index[*last_node]; //direction is a vector from our kart to the sectors we are testing direction = DriveGraph::get()->getNode(target_sector)->getCenter() @@ -2030,12 +2029,10 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) *last_node = m_next_node_index[m_track_node]; float angle = DriveGraph::get()->getAngleToNext(m_track_node, m_successor_index[m_track_node]); - int target_sector; Vec3 direction; Vec3 step_track_coord; - float angle1; // The original while(1) loop is replaced with a for loop to avoid // infinite loops (which we had once or twice). Usually the number // of iterations in the while loop is less than 7. @@ -2043,8 +2040,8 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) { // target_sector is the sector at the longest distance that we can // drive to without crashing with the track. - target_sector = m_next_node_index[*last_node]; - angle1 = DriveGraph::get()->getAngleToNext(target_sector, + int target_sector = m_next_node_index[*last_node]; + float angle1 = DriveGraph::get()->getAngleToNext(target_sector, m_successor_index[target_sector]); // In very sharp turns this algorithm tends to aim at off track points, // resulting in hitting a corner. So test for this special case and diff --git a/src/karts/controller/test_ai.cpp b/src/karts/controller/test_ai.cpp index cdc35616a..eae261d32 100644 --- a/src/karts/controller/test_ai.cpp +++ b/src/karts/controller/test_ai.cpp @@ -1983,7 +1983,6 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) m_curve[CURVE_KART]->addPoint(m_kart->getTrans()(forw)+eps); #endif *last_node = m_next_node_index[m_track_node]; - int target_sector; Vec3 direction; Vec3 step_track_coord; @@ -1995,7 +1994,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) { // target_sector is the sector at the longest distance that we can // drive to without crashing with the track. - target_sector = m_next_node_index[*last_node]; + int target_sector = m_next_node_index[*last_node]; //direction is a vector from our kart to the sectors we are testing direction = DriveGraph::get()->getNode(target_sector)->getCenter() @@ -2079,12 +2078,9 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) *last_node = m_next_node_index[m_track_node]; float angle = DriveGraph::get()->getAngleToNext(m_track_node, m_successor_index[m_track_node]); - int target_sector; - Vec3 direction; Vec3 step_track_coord; - float angle1; // The original while(1) loop is replaced with a for loop to avoid // infinite loops (which we had once or twice). Usually the number // of iterations in the while loop is less than 7. @@ -2092,8 +2088,8 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node) { // target_sector is the sector at the longest distance that we can // drive to without crashing with the track. - target_sector = m_next_node_index[*last_node]; - angle1 = DriveGraph::get()->getAngleToNext(target_sector, + int target_sector = m_next_node_index[*last_node]; + float angle1 = DriveGraph::get()->getAngleToNext(target_sector, m_successor_index[target_sector]); // In very sharp turns this algorithm tends to aim at off track points, // resulting in hitting a corner. So test for this special case and diff --git a/src/karts/kart_model.cpp b/src/karts/kart_model.cpp index 364778518..b14b050ed 100644 --- a/src/karts/kart_model.cpp +++ b/src/karts/kart_model.cpp @@ -926,11 +926,10 @@ void KartModel::update(float dt, float distance, float steer, float speed, m_kart->getKartProperties()->getSpeedWeightedObjectProperties().value_name // Animation strength - float strength = 1.0f; const float strength_factor = GET_VALUE(obj, m_strength_factor); if (strength_factor >= 0.0f) { - strength = speed * strength_factor; + float strength = speed * strength_factor; btClamp(strength, 0.0f, 1.0f); } diff --git a/src/karts/skidding.cpp b/src/karts/skidding.cpp index 58e4466f2..294bc725c 100644 --- a/src/karts/skidding.cpp +++ b/src/karts/skidding.cpp @@ -325,14 +325,10 @@ void Skidding::update(float dt, bool is_on_ground, m_predicted_curve->setVisible(true); m_predicted_curve->setPosition(m_kart->getXYZ()); m_predicted_curve->setHeading(m_kart->getHeading()); - float angle = kp - ->getMaxSteerAngle(m_kart->getSpeed()) - * fabsf(getSteeringFraction()); - angle = kp - ->getMaxSteerAngle(SPEED) + float angle = kp->getMaxSteerAngle(SPEED) * fabsf(getSteeringFraction()); float r = kp->getWheelBase() - / asin(angle)*1.0f; + / asin(angle)*1.0f; const int num_steps = 50; diff --git a/src/main.cpp b/src/main.cpp index d74e5d254..150256216 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -429,7 +429,6 @@ bool isEasterMode(int day, int month, int year, int before_after_days) } return (month > easter_start_month || (month == easter_start_month && day >= easter_start_day)) && (month < easter_end_month || (month == easter_end_month && day <= easter_end_day)); - break; } case 1: return true; break; default: return false; break; diff --git a/src/online/online_player_profile.cpp b/src/online/online_player_profile.cpp index 8996cc694..58fd1efae 100644 --- a/src/online/online_player_profile.cpp +++ b/src/online/online_player_profile.cpp @@ -91,10 +91,9 @@ namespace Online /** Request a login using the saved credentials of the user. */ void OnlinePlayerProfile::requestSavedSession() { - SignInRequest *request = NULL; if (m_online_state == OS_SIGNED_OUT && hasSavedSession()) { - request = new SignInRequest(true); + SignInRequest *request = new SignInRequest(true); setUserDetails(request, "saved-session"); // The userid must be taken from the saved data, diff --git a/src/scriptengine/scriptarray.cpp b/src/scriptengine/scriptarray.cpp index 7689f2b95..322cdfa17 100644 --- a/src/scriptengine/scriptarray.cpp +++ b/src/scriptengine/scriptarray.cpp @@ -975,8 +975,6 @@ bool CScriptArray::Less(const void *a, const void *b, bool asc, asIScriptContext } else { - int r = 0; - if( subTypeId & asTYPEID_OBJHANDLE ) { // Allow sort to work even if the array contains null handles @@ -988,7 +986,7 @@ bool CScriptArray::Less(const void *a, const void *b, bool asc, asIScriptContext if( cache && cache->cmpFunc ) { // TODO: Add proper error handling - r = ctx->Prepare(cache->cmpFunc); assert(r >= 0); + int r = ctx->Prepare(cache->cmpFunc); assert(r >= 0); if( subTypeId & asTYPEID_OBJHANDLE ) { diff --git a/src/states_screens/edit_track_screen.cpp b/src/states_screens/edit_track_screen.cpp index 1b9863767..9147a008c 100644 --- a/src/states_screens/edit_track_screen.cpp +++ b/src/states_screens/edit_track_screen.cpp @@ -177,8 +177,6 @@ void EditTrackScreen::init() // ----------------------------------------------------------------------------- void EditTrackScreen::loadTrackList() { - bool belongs_to_group; - DynamicRibbonWidget* tracks_widget = getWidget("tracks"); assert(tracks_widget != NULL); @@ -187,7 +185,7 @@ void EditTrackScreen::loadTrackList() for (unsigned int i = 0; i < track_manager->getNumberOfTracks(); i++) { Track* t = track_manager->getTrack(i); - belongs_to_group = (m_track_group.empty() || + bool belongs_to_group = (m_track_group.empty() || m_track_group == ALL_TRACKS_GROUP_ID || t->isInGroup(m_track_group) ); if (!t->isArena() && !t->isSoccer() && diff --git a/src/states_screens/kart_selection.cpp b/src/states_screens/kart_selection.cpp index b93d5c98b..cf53a07be 100644 --- a/src/states_screens/kart_selection.cpp +++ b/src/states_screens/kart_selection.cpp @@ -1158,13 +1158,12 @@ void KartSelectionScreen::allPlayersDone() if (selected_kart == RANDOM_KART_ID) { // don't select an already selected kart - int random_id; // to prevent infinite loop in case they are all locked int count = 0; bool done = false; do { - random_id = random.get(item_count); + int random_id = random.get(item_count); // valid kart if it can bt used, and is either not locked, // or it's a multiplayer race. if (items[random_id].m_code_name != ID_DONT_USE && diff --git a/src/states_screens/options_screen_device.cpp b/src/states_screens/options_screen_device.cpp index 85de146e2..0101bf300 100644 --- a/src/states_screens/options_screen_device.cpp +++ b/src/states_screens/options_screen_device.cpp @@ -621,11 +621,10 @@ bool OptionsScreenDevice::conflictsBetweenKbdConfig(PlayerAction action, PlayerAction from, PlayerAction to) { - KeyboardConfig* other_kbd_config; int id = m_config->getBinding(action).getId(); for (int i=0; i < input_manager->getDeviceManager()->getKeyboardAmount(); i++) { - other_kbd_config = + KeyboardConfig* other_kbd_config = input_manager->getDeviceManager()->getKeyboardConfig(i); if (m_config != other_kbd_config && From d21630c8156cdffe8ba80b2c73ba7b7fee7be2af Mon Sep 17 00:00:00 2001 From: Nado Date: Fri, 16 Sep 2016 12:49:27 +0200 Subject: [PATCH 266/350] This simplifies the reading of `bool isEasterMode()` --- src/main.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 150256216..c79e4195c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -395,9 +395,11 @@ void handleXmasMode() */ bool isEasterMode(int day, int month, int year, int before_after_days) { - switch (UserConfigParams::m_easter_ear_mode) - { - case 0: + if (UserConfigParams::m_easter_ear_mode == 1) { + return true; + } + + if (UserConfigParams::m_easter_ear_mode == 0) { // Compute Easter date, based on wikipedia formula // http://en.wikipedia.org/wiki/Computus @@ -430,10 +432,8 @@ bool isEasterMode(int day, int month, int year, int before_after_days) return (month > easter_start_month || (month == easter_start_month && day >= easter_start_day)) && (month < easter_end_month || (month == easter_end_month && day <= easter_end_day)); } - case 1: return true; break; - default: return false; break; - } // switch m_xmas_mode + return false; } // isEasterMode(day, month, year, before_after_days) // ============================================================================ From ceb961ae87f6050b964882dd9b0671bb662d1b04 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 5 Oct 2016 13:31:31 +1100 Subject: [PATCH 267/350] Fixed reverse camera (smooth speed was positive even when going backwards). --- src/karts/kart.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 7ef3a3861..05bbe88ab 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -1566,8 +1566,6 @@ void Kart::updateSpeed() // only in the order of centimetres though). Smoothing the speed value // gets rid of this jitter, and also r m_speed = getVehicle()->getRigidBody()->getLinearVelocity().length(); - float f = 0.3f; - m_smoothed_speed = f*m_speed + (1.0f - f)*m_smoothed_speed; // calculate direction of m_speed const btTransform& chassisTrans = getVehicle()->getChassisWorldTransform(); @@ -1581,6 +1579,9 @@ void Kart::updateSpeed() m_speed = -m_speed; } + float f = 0.3f; + m_smoothed_speed = f*m_speed + (1.0f - f)*m_smoothed_speed; + // At low velocity, forces on kart push it back and forth so we ignore this // - quick'n'dirty workaround for bug 1776883 if (fabsf(m_speed) < 0.2f || From 43d9db4db081888891051533ebc09334b8c2152b Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 5 Oct 2016 11:53:45 +0800 Subject: [PATCH 268/350] Fix acceleration and braking of ArenaAI --- src/karts/controller/arena_ai.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/karts/controller/arena_ai.cpp b/src/karts/controller/arena_ai.cpp index 0cec89df1..0785829ff 100644 --- a/src/karts/controller/arena_ai.cpp +++ b/src/karts/controller/arena_ai.cpp @@ -332,7 +332,6 @@ void ArenaAI::configSpeed() const float MIN_SPEED = 5.0f; const float handicap = (m_cur_difficulty == RaceManager::DIFFICULTY_EASY ? 0.7f : 1.0f ); - m_controls->setAccel(stk_config->m_ai_acceleration * handicap); const float max_turn_speed = m_kart->getSpeedForTurnRadius(m_turn_radius); if ((m_kart->getSpeed() > max_turn_speed || forceBraking()) && From a91af96637c414a24a6c62bad60a8b0f2deb0e62 Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 5 Oct 2016 16:18:39 +0800 Subject: [PATCH 269/350] Initial work on spare tire kart in battle mode Some values are hard-coded for now --- sources.cmake | 2 +- src/items/item.hpp | 3 + src/items/item_manager.cpp | 3 + src/karts/controller/arena_ai.cpp | 3 +- src/karts/controller/arena_ai.hpp | 1 + src/karts/controller/battle_ai.cpp | 14 --- src/karts/controller/battle_ai.hpp | 12 +-- src/karts/controller/spare_tire_ai.cpp | 134 +++++++++++++++++++++++++ src/karts/controller/spare_tire_ai.hpp | 50 +++++++++ src/karts/kart.cpp | 5 + src/modes/three_strikes_battle.cpp | 131 ++++++++++++++++++++++-- src/modes/three_strikes_battle.hpp | 6 ++ src/modes/world.cpp | 6 +- src/modes/world_with_rank.cpp | 7 +- src/utils/debug.cpp | 3 - 15 files changed, 344 insertions(+), 36 deletions(-) create mode 100644 src/karts/controller/spare_tire_ai.cpp create mode 100644 src/karts/controller/spare_tire_ai.hpp diff --git a/sources.cmake b/sources.cmake index ddc029d4f..f484b15d5 100644 --- a/sources.cmake +++ b/sources.cmake @@ -1,5 +1,5 @@ # Modify this file to change the last-modified date when you add/remove a file. -# This will then trigger a new cmake run automatically. +# This will then trigger a new cmake run automatically. file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp") file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp") file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*") diff --git a/src/items/item.hpp b/src/items/item.hpp index 763b3008c..99b1b3abf 100644 --- a/src/items/item.hpp +++ b/src/items/item.hpp @@ -276,6 +276,9 @@ public: { return (scene::ISceneNode *) m_node; } + // ------------------------------------------------------------------------ + const btQuaternion& getRotation() const { return m_original_rotation; } + }; // class Item #endif diff --git a/src/items/item_manager.cpp b/src/items/item_manager.cpp index 61c5e3232..6afb9733d 100644 --- a/src/items/item_manager.cpp +++ b/src/items/item_manager.cpp @@ -29,6 +29,7 @@ #include "graphics/material_manager.hpp" #include "io/file_manager.hpp" #include "karts/abstract_kart.hpp" +#include "karts/controller/spare_tire_ai.hpp" #include "modes/linear_world.hpp" #include "network/network_config.hpp" #include "network/race_event_manager.hpp" @@ -287,6 +288,8 @@ Item* ItemManager::newItem(const Vec3& xyz, float distance, void ItemManager::collectedItem(Item *item, AbstractKart *kart, int add_info) { assert(item); + // Spare tire karts don't collect items + if (dynamic_cast(kart->getController()) != NULL) return; if( (item->getType() == Item::ITEM_BUBBLEGUM || item->getType() == Item::ITEM_BUBBLEGUM_NOLOK) && kart->isShielded()) { diff --git a/src/karts/controller/arena_ai.cpp b/src/karts/controller/arena_ai.cpp index 0785829ff..675028c3d 100644 --- a/src/karts/controller/arena_ai.cpp +++ b/src/karts/controller/arena_ai.cpp @@ -343,7 +343,8 @@ void ArenaAI::configSpeed() else { // Otherwise accelerate - m_controls->setAccel(stk_config->m_ai_acceleration * handicap); + m_controls->setAccel(stk_config->m_ai_acceleration * handicap * + getSpeedCap()); } } // configSpeed diff --git a/src/karts/controller/arena_ai.hpp b/src/karts/controller/arena_ai.hpp index 40adaaac5..6a772155f 100644 --- a/src/karts/controller/arena_ai.hpp +++ b/src/karts/controller/arena_ai.hpp @@ -128,6 +128,7 @@ private: virtual bool isWaiting() const = 0; virtual bool isKartOnRoad() const = 0; virtual void resetAfterStop() {}; + virtual float getSpeedCap() const { return 1.0f; } public: ArenaAI(AbstractKart *kart); virtual ~ArenaAI() {}; diff --git a/src/karts/controller/battle_ai.cpp b/src/karts/controller/battle_ai.cpp index f3cfe7aa7..e5106fdfd 100644 --- a/src/karts/controller/battle_ai.cpp +++ b/src/karts/controller/battle_ai.cpp @@ -64,20 +64,6 @@ BattleAI::~BattleAI() #endif } // ~BattleAI -//----------------------------------------------------------------------------- -/** Resets the AI when a race is restarted. - */ -void BattleAI::reset() -{ - ArenaAI::reset(); -} // reset - -//----------------------------------------------------------------------------- -void BattleAI::update(float dt) -{ - ArenaAI::update(dt); -} // update - //----------------------------------------------------------------------------- void BattleAI::findClosestKart(bool use_difficulty) { diff --git a/src/karts/controller/battle_ai.hpp b/src/karts/controller/battle_ai.hpp index 5a89a5112..d95e1144c 100644 --- a/src/karts/controller/battle_ai.hpp +++ b/src/karts/controller/battle_ai.hpp @@ -30,21 +30,19 @@ class ThreeStrikesBattle; */ class BattleAI : public ArenaAI { -private: +protected: /** Keep a pointer to world. */ ThreeStrikesBattle *m_world; - + virtual int getCurrentNode() const OVERRIDE; +private: virtual void findClosestKart(bool use_difficulty) OVERRIDE; virtual void findTarget() OVERRIDE; - virtual int getCurrentNode() const OVERRIDE; virtual float getKartDistance(const AbstractKart* kart) const OVERRIDE; virtual bool isKartOnRoad() const OVERRIDE; virtual bool isWaiting() const OVERRIDE; public: - BattleAI(AbstractKart *kart); - ~BattleAI(); - virtual void update (float delta) OVERRIDE; - virtual void reset () OVERRIDE; + BattleAI(AbstractKart *kart); + ~BattleAI(); }; #endif diff --git a/src/karts/controller/spare_tire_ai.cpp b/src/karts/controller/spare_tire_ai.cpp new file mode 100644 index 000000000..21f994e51 --- /dev/null +++ b/src/karts/controller/spare_tire_ai.cpp @@ -0,0 +1,134 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "karts/controller/spare_tire_ai.hpp" + +#include "karts/abstract_kart.hpp" +#include "karts/kart_gfx.hpp" +#include "modes/three_strikes_battle.hpp" +#include "tracks/arena_graph.hpp" +#include "tracks/arena_node.hpp" +#include "physics/physics.hpp" +#include "utils/random_generator.hpp" + +SpareTireAI::SpareTireAI(AbstractKart *kart) + : BattleAI(kart) +{ + reset(); + // Don't call our own setControllerName, since this will add a + // billboard showing 'AIBaseController' to the kart. + Controller::setControllerName("SpareTireAI"); + +} // SpareTireAI + +//----------------------------------------------------------------------------- +/** Resets the AI when a race is restarted. + */ +void SpareTireAI::reset() +{ + BattleAI::reset(); + m_fixed_target_nodes.clear(); + m_idx = 0; + m_timer = 0.0f; +} // reset + +//----------------------------------------------------------------------------- +void SpareTireAI::update(float dt) +{ + assert(!m_fixed_target_nodes.empty()); + BattleAI::update(dt); + m_timer -= dt; + if (m_timer < 0.0f) + unspawn(); +} // update + +//----------------------------------------------------------------------------- +void SpareTireAI::findDefaultPath() +{ + // Randomly find 3 nodes for spare tire kart to move + assert(m_fixed_target_nodes.empty()); + const int nodes = m_graph->getNumNodes(); + const float min_dist = sqrtf(nodes); + RandomGenerator random; + while (m_fixed_target_nodes.size() < 3) + { + int node = random.get(nodes); + if (m_fixed_target_nodes.empty()) + { + m_fixed_target_nodes.push_back(node); + continue; + } + bool succeed = true; + for (const int& all_node : m_fixed_target_nodes) + { + float dist = m_graph->getDistance(all_node, node); + if (dist < min_dist) + succeed = false; + } + if (succeed) + m_fixed_target_nodes.push_back(node); + } + m_idx = 0; + m_target_node = m_fixed_target_nodes[m_idx]; + +} // findDefaultPath + +//----------------------------------------------------------------------------- +void SpareTireAI::findTarget() +{ + if (getCurrentNode() == m_fixed_target_nodes[m_idx]) + m_idx = m_idx == 2 ? 0 : m_idx + 1; + + const int chosen_node = m_fixed_target_nodes[m_idx]; + m_target_node = chosen_node; + m_target_point = m_graph->getNode(chosen_node)->getCenter(); +} // findTarget + +//----------------------------------------------------------------------------- +void SpareTireAI::spawn(float time_to_last) +{ + findDefaultPath(); + m_timer = time_to_last; + + World::getWorld()->getPhysics()->addKart(m_kart); + m_kart->getKartGFX()->reset(); + m_kart->getNode()->setVisible(true); + +} // spawn + +//----------------------------------------------------------------------------- +void SpareTireAI::unspawn() +{ + reset(); + m_kart->eliminate(); +} // unspawn + +//----------------------------------------------------------------------------- +void SpareTireAI::crashed(const AbstractKart *k) +{ + // Nothing happen when two spare tire karts crash each other + if (dynamic_cast(k->getController()) != NULL) return; + + // Max 3 lives only + if (m_world->getKartLife(k->getWorldKartId()) == 3) return; + + // Otherwise increase one life for that kart and unspawn + m_world->addKartLife(k->getWorldKartId()); + unspawn(); + +} // crashed diff --git a/src/karts/controller/spare_tire_ai.hpp b/src/karts/controller/spare_tire_ai.hpp new file mode 100644 index 000000000..a8d7cb76a --- /dev/null +++ b/src/karts/controller/spare_tire_ai.hpp @@ -0,0 +1,50 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2016 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_SPARE_TIRE_AI_HPP +#define HEADER_SPARE_TIRE_AI_HPP + +#include "karts/controller/battle_ai.hpp" + +/** The AI for spare tire karts in battle mode, allowing kart to gain life. + * \ingroup controller + */ +class SpareTireAI : public BattleAI +{ +private: + std::vector m_fixed_target_nodes; + + int m_idx; + + float m_timer; + + virtual void findClosestKart(bool use_difficulty) OVERRIDE {} + virtual void findTarget() OVERRIDE; + virtual float getSpeedCap() const OVERRIDE { return 0.7f; } + void findDefaultPath(); +public: + SpareTireAI(AbstractKart *kart); + virtual void crashed(const AbstractKart *k) OVERRIDE; + virtual void update(float delta) OVERRIDE; + virtual void reset() OVERRIDE; + void spawn(float time_to_last); + void unspawn(); + bool needUpdate() const { return !m_fixed_target_nodes.empty(); } +}; + +#endif diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 05bbe88ab..ea2ef13b4 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -50,6 +50,7 @@ #include "karts/abstract_kart_animation.hpp" #include "karts/cached_characteristic.hpp" #include "karts/controller/end_controller.hpp" +#include "karts/controller/spare_tire_ai.hpp" #include "karts/explosion_animation.hpp" #include "karts/kart_gfx.hpp" #include "karts/kart_model.hpp" @@ -864,6 +865,10 @@ void Kart::finishedRace(float time, bool from_server) */ m_finished_race = true; m_finish_time = time; + + // If this is spare tire kart, end now + if (dynamic_cast(m_controller) != NULL) return; + m_controller->finishedRace(time); m_kart_model->finishedRace(); race_manager->kartFinishedRace(this, time); diff --git a/src/modes/three_strikes_battle.cpp b/src/modes/three_strikes_battle.cpp index f8bf91434..15837238e 100644 --- a/src/modes/three_strikes_battle.cpp +++ b/src/modes/three_strikes_battle.cpp @@ -23,13 +23,17 @@ #include "graphics/camera.hpp" #include "graphics/irr_driver.hpp" #include "io/file_manager.hpp" -#include "karts/abstract_kart.hpp" +#include "items/item_manager.hpp" +#include "karts/kart.hpp" +#include "karts/controller/spare_tire_ai.hpp" #include "karts/kart_model.hpp" #include "karts/kart_properties.hpp" #include "physics/physics.hpp" #include "states_screens/race_gui_base.hpp" +#include "tracks/arena_graph.hpp" #include "tracks/track.hpp" #include "tracks/track_object_manager.hpp" +#include "tracks/track_sector.hpp" #include "utils/constants.hpp" #include @@ -63,6 +67,41 @@ void ThreeStrikesBattle::init() { WorldWithRank::init(); m_display_rank = false; + + // Spare tire karts only added with large arena + const int all_nodes = + ArenaGraph::get() ? ArenaGraph::get()->getNumNodes() : 0; + if (all_nodes > 200) + { + const unsigned int max_sta_num = unsigned(m_karts.size() * 0.8f); + for (int i = 0; i < all_nodes; i++) + { + // Pre-spawn the spare tire karts on the item position, preventing + // affecting current karts + Item* item = ItemManager::get()->getFirstItemInQuad(i); + if (item == NULL) continue; + btTransform t; + t.setOrigin(item->getXYZ()); + t.setRotation(item->getRotation()); + + AbstractKart* sta = new Kart("nolok", m_karts.size(), + m_karts.size() + 1, t, PLAYER_DIFFICULTY_NORMAL, KRT_BLUE); + sta->init(RaceManager::KartType::KT_AI); + sta->setController(new SpareTireAI(sta)); + + m_karts.push_back(sta); + m_spare_tire_karts.push_back(sta); + m_kart_track_sector.push_back(new TrackSector()); + m_position_index.push_back(0); +#ifdef DEBUG + m_position_used.push_back(false); +#endif + m_track->adjustForFog(sta->getNode()); + + if (m_spare_tire_karts.size() >= max_sta_num) break; + } + } + m_kart_info.resize(m_karts.size()); } // ThreeStrikesBattle @@ -73,6 +112,7 @@ void ThreeStrikesBattle::init() ThreeStrikesBattle::~ThreeStrikesBattle() { m_tires.clearWithoutDeleting(); + m_spare_tire_karts.clear(); irr_driver->grabAllTextures(m_tire); // Remove the mesh from the cache so that the mesh is properly @@ -88,14 +128,27 @@ void ThreeStrikesBattle::reset() { WorldWithRank::reset(); + m_sta_spawned_count = 1; const unsigned int kart_amount = (unsigned int)m_karts.size(); for(unsigned int n=0; nsetPosition(-1); + // Eliminate all spare tire karts first, they will be spawned if needed + bool is_sta = false; + if (dynamic_cast(m_karts[n]->getController()) != NULL) + { + m_kart_info[n].m_lives = -1; + m_karts[n]->setPosition(-1); + m_karts[n]->finishedRace(0.0f); + eliminateKart(n, /*notify_of_elimination*/ false); + is_sta = true; + } + else + { + m_kart_info[n].m_lives = 3; + // no positions in this mode + m_karts[n]->setPosition(-1); + } scene::ISceneNode* kart_node = m_karts[n]->getNode(); @@ -107,11 +160,11 @@ void ThreeStrikesBattle::reset() if (core::stringc(curr->getName()) == "tire1") { - curr->setVisible(true); + curr->setVisible(!is_sta); } else if (core::stringc(curr->getName()) == "tire2") { - curr->setVisible(true); + curr->setVisible(!is_sta); } } @@ -165,6 +218,15 @@ void ThreeStrikesBattle::kartHit(const unsigned int kart_id) { if (isRaceOver()) return; + SpareTireAI* sta = + dynamic_cast(m_karts[kart_id]->getController()); + if (sta) + { + // Unspawn the spare tire kart if it get hit + sta->unspawn(); + return; + } + assert(kart_id < m_karts.size()); // make kart lose a life, ignore if in profiling mode if (!UserConfigParams::m_arena_ai_stats) @@ -305,6 +367,37 @@ void ThreeStrikesBattle::update(float dt) WorldWithRank::update(dt); WorldWithRank::updateTrack(dt); + const float period = 20.0f; + if (!m_spare_tire_karts.empty() && + period < getTimeSinceStart() / float(m_sta_spawned_count)) + { + // Spawn spare tire kart when necessary + m_sta_spawned_count++; + // Formula : Total num of karts with life != 3 * + // time period / time since start, so towards the end of game, + // karts are less likely to gain back a life. + int kart_has_few_lives = 0; + for (unsigned int i = 0; i < m_kart_info.size(); i++) + m_kart_info[i].m_lives != 3 ? kart_has_few_lives++ : 0; + float ratio = kart_has_few_lives * period / getTimeSinceStart(); + if (ratio > 1.0f) + { + unsigned int spawn_sta = unsigned(ratio); + if (spawn_sta > m_spare_tire_karts.size()) + spawn_sta = m_spare_tire_karts.size(); + m_race_gui->addMessage(_P("%i spare tire kart has been spawned!", + "%i spare tire karts have been spawned!", + spawn_sta), NULL, 2.0f); + for (unsigned int i = 0; i < spawn_sta; i++) + { + SpareTireAI* sta = dynamic_cast + (m_spare_tire_karts[i]->getController()); + assert(sta); + sta->spawn(period / 2); + } + } + } + if (m_track->hasNavMesh()) updateSectorForKarts(); @@ -476,6 +569,8 @@ void ThreeStrikesBattle::getKartsDisplayInfo( const unsigned int kart_amount = getNumKarts(); for(unsigned int i = 0; i < kart_amount ; i++) { + if (dynamic_cast(m_karts[i]->getController()) != NULL) + continue; RaceGUIBase::KartIconDisplayInfo& rank_info = (*info)[i]; // reset color @@ -509,6 +604,16 @@ void ThreeStrikesBattle::enterRaceOverState() { WorldWithRank::enterRaceOverState(); + // Unspawn all spare tire karts if neccesary + for (unsigned int i = 0; i < m_spare_tire_karts.size(); i++) + { + SpareTireAI* sta = + dynamic_cast(m_spare_tire_karts[i]->getController()); + assert(sta); + if (sta->needUpdate()) + sta->unspawn(); + } + if (UserConfigParams::m_arena_ai_stats) { float runtime = (irr_driver->getRealTime()-m_start_time)*0.001f; @@ -521,3 +626,15 @@ void ThreeStrikesBattle::enterRaceOverState() } } // enterRaceOverState + +//----------------------------------------------------------------------------- +bool ThreeStrikesBattle::spareTireKartsSpawned() const +{ + // Spare tire karts are spawned if at least 1 of them needs update + assert(!m_spare_tire_karts.empty()); + SpareTireAI* sta = + dynamic_cast(m_spare_tire_karts[0]->getController()); + assert(sta); + + return sta->needUpdate(); +} // spareTireKartsSpawned diff --git a/src/modes/three_strikes_battle.hpp b/src/modes/three_strikes_battle.hpp index 0acfffd43..2f839f465 100644 --- a/src/modes/three_strikes_battle.hpp +++ b/src/modes/three_strikes_battle.hpp @@ -77,6 +77,9 @@ private: int m_start_time; int m_total_hit; + std::vector m_spare_tire_karts; + int m_sta_spawned_count; + public: /** Used to show a nice graph when battle is over */ @@ -115,6 +118,9 @@ public: void updateKartRanks(); void increaseRescueCount() { m_total_rescue++; } + void addKartLife(unsigned int id) { m_kart_info[id].m_lives++; } + int getKartLife(unsigned int id) const { return m_kart_info[id].m_lives; } + bool spareTireKartsSpawned() const; }; // ThreeStrikesBattles diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 392d7ee83..592f3914b 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -38,6 +38,7 @@ #include "karts/controller/end_controller.hpp" #include "karts/controller/local_player_controller.hpp" #include "karts/controller/skidding_ai.hpp" +#include "karts/controller/spare_tire_ai.hpp" #include "karts/controller/test_ai.hpp" #include "karts/controller/network_player_controller.hpp" #include "karts/kart.hpp" @@ -971,8 +972,11 @@ void World::update(float dt) const int kart_amount = (int)m_karts.size(); for (int i = 0 ; i < kart_amount; ++i) { + SpareTireAI* sta = + dynamic_cast(m_karts[i]->getController()); // Update all karts that are not eliminated - if(!m_karts[i]->isEliminated()) m_karts[i]->update(dt) ; + if(!m_karts[i]->isEliminated() || (sta && sta->needUpdate())) + m_karts[i]->update(dt); } PROFILER_POP_CPU_MARKER(); diff --git a/src/modes/world_with_rank.cpp b/src/modes/world_with_rank.cpp index cb84ae2da..e03835954 100644 --- a/src/modes/world_with_rank.cpp +++ b/src/modes/world_with_rank.cpp @@ -18,6 +18,7 @@ #include "modes/world_with_rank.hpp" #include "karts/abstract_kart.hpp" +#include "karts/controller/spare_tire_ai.hpp" #include "karts/kart_properties.hpp" #include "race/history.hpp" #include "tracks/graph.hpp" @@ -251,7 +252,9 @@ void WorldWithRank::updateSectorForKarts() assert(n == m_kart_track_sector.size()); for (unsigned int i = 0; i < n; i++) { - if (m_karts[i]->isEliminated()) continue; - getTrackSector(i)->update(m_karts[i]->getXYZ()); + SpareTireAI* sta = + dynamic_cast(m_karts[i]->getController()); + if (!m_karts[i]->isEliminated() || (sta && sta->needUpdate())) + getTrackSector(i)->update(m_karts[i]->getXYZ()); } } // updateSectorForKarts diff --git a/src/utils/debug.cpp b/src/utils/debug.cpp index 7cd03a8c8..a83fbb161 100644 --- a/src/utils/debug.cpp +++ b/src/utils/debug.cpp @@ -182,12 +182,9 @@ void changeCameraTarget(u32 num) { AbstractKart* kart = world->getKart(num - 1); if (kart == NULL) return; - if (kart->isEliminated()) return; cam->setMode(Camera::CM_NORMAL); cam->setKart(kart); } - else - return; } // changeCameraTarget From a0b02659f716858c852fd855d97b1d62fcf77622 Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 5 Oct 2016 16:42:37 +0800 Subject: [PATCH 270/350] Don't use num of karts from world, as it includes spare tire karts --- src/race/history.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/race/history.cpp b/src/race/history.cpp index 85ce6492a..30ebbcbd4 100644 --- a/src/race/history.cpp +++ b/src/race/history.cpp @@ -96,7 +96,7 @@ void History::updateSaving(float dt) m_all_deltas[m_current] = dt; World *world = World::getWorld(); - unsigned int num_karts = world->getNumKarts(); + unsigned int num_karts = race_manager->getNumberOfKarts(); unsigned int index = m_current*num_karts; for(unsigned int i=0; i Date: Wed, 5 Oct 2016 10:50:20 +0200 Subject: [PATCH 271/350] Fixed rare case that anisotropic filtering feature is not available in graphics drivers. --- src/graphics/central_settings.cpp | 19 ++++-- src/graphics/central_settings.hpp | 6 +- src/graphics/graphics_restrictions.cpp | 5 +- src/graphics/graphics_restrictions.hpp | 3 +- src/graphics/texture_shader.cpp | 85 +++++++++++++++++--------- 5 files changed, 81 insertions(+), 37 deletions(-) diff --git a/src/graphics/central_settings.cpp b/src/graphics/central_settings.cpp index 8d17a1edc..e398bafdd 100644 --- a/src/graphics/central_settings.cpp +++ b/src/graphics/central_settings.cpp @@ -49,7 +49,8 @@ void CentralVideoSettings::init() hasUBO = false; hasExplicitAttribLocation = false; hasGS = false; - + hasTextureFilterAnisotropic = false; + #if defined(USE_GLES2) hasBGRA = false; hasColorBufferFloat = false; @@ -89,7 +90,7 @@ void CentralVideoSettings::init() std::string card((char*)(glGetString(GL_RENDERER))); GraphicsRestrictions::init(driver, card); -#if !defined(USE_GLES2) +#if !defined(USE_GLES2) if (hasGLExtension("GL_AMD_vertex_shader_layer")) { hasVSLayer = true; Log::info("GLDriver", "AMD Vertex Shader Layer Present"); @@ -170,6 +171,11 @@ void CentralVideoSettings::init() hasExplicitAttribLocation = true; Log::info("GLDriver", "ARB Explicit Attrib Location Present"); } + if (!GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_TEXTURE_FILTER_ANISOTROPIC) && + hasGLExtension("GL_EXT_texture_filter_anisotropic")) { + hasTextureFilterAnisotropic = true; + Log::info("GLDriver", "EXT Texture Filter Anisotropic Present"); + } if (!GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_GEOMETRY_SHADER) && (m_gl_major_version > 3 || (m_gl_major_version == 3 && m_gl_minor_version >= 2))) { hasGS = true; @@ -223,7 +229,7 @@ void CentralVideoSettings::init() hasAtomics = true; hasSSBO = true; } - + if (!GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_TEXTURE_FORMAT_BGRA8888) && (hasGLExtension("GL_IMG_texture_format_BGRA8888") || hasGLExtension("GL_EXT_texture_format_BGRA8888"))) @@ -231,7 +237,7 @@ void CentralVideoSettings::init() hasBGRA = true; Log::info("GLDriver", "EXT texture format BGRA8888 Present"); } - + if (!GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_COLOR_BUFFER_FLOAT) && hasGLExtension("GL_EXT_color_buffer_float")) { @@ -357,6 +363,11 @@ bool CentralVideoSettings::isARBMultiDrawIndirectUsable() const return hasMultiDrawIndirect; } +bool CentralVideoSettings::isEXTTextureFilterAnisotropicUsable() const +{ + return hasTextureFilterAnisotropic; +} + #if defined(USE_GLES2) bool CentralVideoSettings::isEXTTextureFormatBGRA8888Usable() const { diff --git a/src/graphics/central_settings.hpp b/src/graphics/central_settings.hpp index b9e8d1526..91e71beb9 100644 --- a/src/graphics/central_settings.hpp +++ b/src/graphics/central_settings.hpp @@ -42,7 +42,8 @@ private: bool hasSSBO; bool hasImageLoadStore; bool hasMultiDrawIndirect; - + bool hasTextureFilterAnisotropic; + #if defined(USE_GLES2) bool hasBGRA; bool hasColorBufferFloat; @@ -80,7 +81,8 @@ public: bool isARBImageLoadStoreUsable() const; bool isARBMultiDrawIndirectUsable() const; bool isARBExplicitAttribLocationUsable() const; - + bool isEXTTextureFilterAnisotropicUsable() const; + #if defined(USE_GLES2) bool isEXTTextureFormatBGRA8888Usable() const; bool isEXTColorBufferFloatUsable() const; diff --git a/src/graphics/graphics_restrictions.cpp b/src/graphics/graphics_restrictions.cpp index 83b6be731..9bd290e65 100644 --- a/src/graphics/graphics_restrictions.cpp +++ b/src/graphics/graphics_restrictions.cpp @@ -57,6 +57,7 @@ namespace GraphicsRestrictions "TextureCompressionS3TC", "AMDVertexShaderLayer", "ExplicitAttribLocation", + "TextureFilterAnisotropic", #if defined(USE_GLES2) "TextureFormatBGRA8888", "ColorBufferFloat", @@ -215,7 +216,7 @@ public: convertVersionString(s[0]); return; } - + Log::warn("Graphics", "Can not find version for '%s' '%s' - ignored.", driver_version.c_str(), card_name.c_str()); @@ -299,7 +300,7 @@ public: { m_version_test = VERSION_IGNORE; m_card_test = CARD_IGNORE; - + if(rule->get("is", &m_card_name)) { m_card_test = CARD_IS; diff --git a/src/graphics/graphics_restrictions.hpp b/src/graphics/graphics_restrictions.hpp index 4ae195c6e..c98facfad 100644 --- a/src/graphics/graphics_restrictions.hpp +++ b/src/graphics/graphics_restrictions.hpp @@ -32,7 +32,7 @@ namespace GraphicsRestrictions * the variable m_names_of_restrictions in the cpp file contains the * string representation used in the XML files. Any change to this * type declaration needs a change in that variable as well. */ - enum GraphicsRestrictionsType + enum GraphicsRestrictionsType { GR_UNIFORM_BUFFER_OBJECT, GR_GEOMETRY_SHADER, @@ -51,6 +51,7 @@ namespace GraphicsRestrictions GR_EXT_TEXTURE_COMPRESSION_S3TC, GR_AMD_VERTEX_SHADER_LAYER, GR_EXPLICIT_ATTRIB_LOCATION, + GR_TEXTURE_FILTER_ANISOTROPIC, #if defined(USE_GLES2) GR_TEXTURE_FORMAT_BGRA8888, GR_COLOR_BUFFER_FLOAT, diff --git a/src/graphics/texture_shader.cpp b/src/graphics/texture_shader.cpp index 63f737928..e4de23ae8 100644 --- a/src/graphics/texture_shader.cpp +++ b/src/graphics/texture_shader.cpp @@ -17,6 +17,7 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "graphics/texture_shader.hpp" +#include "graphics/central_settings.hpp" #include "config/user_config.hpp" @@ -33,7 +34,7 @@ TextureShaderBase::BindFunction TextureShaderBase::m_all_bind_functions[] = /* ST_SEMI_TRILINEAR */ &TextureShaderBase::bindTextureSemiTrilinear }; -GLuint TextureShaderBase::m_all_texture_types[] = +GLuint TextureShaderBase::m_all_texture_types[] = { /* ST_NEAREST_FILTERED */ GL_TEXTURE_2D, /* ST_TRILINEAR_ANISOTROPIC_FILTERED */ GL_TEXTURE_2D, /* ST_TRILINEAR_CUBEMAP */ GL_TEXTURE_CUBE_MAP, @@ -55,7 +56,8 @@ void TextureShaderBase::bindTextureNearest(GLuint texture_unit, GLuint tex) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.); + if (CVS->isEXTTextureFilterAnisotropicUsable()) + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.); } // bindTextureNearest @@ -69,9 +71,13 @@ void TextureShaderBase::bindTextureTrilinearAnisotropic(GLuint tex_unit, GLuint glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - int aniso = UserConfigParams::m_anisotropic; - if (aniso == 0) aniso = 1; - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)aniso); + if (CVS->isEXTTextureFilterAnisotropicUsable()) + { + int aniso = UserConfigParams::m_anisotropic; + if (aniso == 0) aniso = 1; + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, + (float)aniso); + } } // bindTextureTrilinearAnisotropic // ---------------------------------------------------------------------------- @@ -84,10 +90,13 @@ void TextureShaderBase::bindCubemapTrilinear(unsigned tex_unit, unsigned tex) glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_REPEAT); - int aniso = UserConfigParams::m_anisotropic; - if (aniso == 0) aniso = 1; - glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, - (float)aniso); + if (CVS->isEXTTextureFilterAnisotropicUsable()) + { + int aniso = UserConfigParams::m_anisotropic; + if (aniso == 0) aniso = 1; + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, + (float)aniso); + } } // bindCubemapTrilinear // ---------------------------------------------------------------------------- @@ -100,7 +109,8 @@ void TextureShaderBase::bindTextureNearestClamped(GLuint texture_unit, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.); + if (CVS->isEXTTextureFilterAnisotropicUsable()) + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.); } // bindTextureNearestClamped // ---------------------------------------------------------------------------- @@ -114,7 +124,8 @@ void TextureShaderBase::bindTextureBilinear(GLuint texture_unit, GLuint tex) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.); + if (CVS->isEXTTextureFilterAnisotropicUsable()) + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.); } // bindTextureBilinear // ---------------------------------------------------------------------------- @@ -126,7 +137,8 @@ void TextureShaderBase::bindTextureBilinearClamped(GLuint tex_unit, GLuint tex) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.); + if (CVS->isEXTTextureFilterAnisotropicUsable()) + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.); } // bindTextureBilinearClamped // ---------------------------------------------------------------------------- @@ -138,7 +150,8 @@ void TextureShaderBase::bindTextureSemiTrilinear(GLuint tex_unit, GLuint tex_id) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.); + if (CVS->isEXTTextureFilterAnisotropicUsable()) + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.); } // bindTextureSemiTrilinear // ---------------------------------------------------------------------------- @@ -165,9 +178,13 @@ void TextureShaderBase::bindTrilinearClampedArrayTexture(unsigned tex_unit, glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - int aniso = UserConfigParams::m_anisotropic; - if (aniso == 0) aniso = 1; - glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)aniso); + if (CVS->isEXTTextureFilterAnisotropicUsable()) + { + int aniso = UserConfigParams::m_anisotropic; + if (aniso == 0) aniso = 1; + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, + (float)aniso); + } } // bindTrilinearClampedArrayTexture // ---------------------------------------------------------------------------- @@ -179,7 +196,8 @@ void TextureShaderBase::bindTextureVolume(unsigned tex_unit, unsigned tex_id) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.); + if (CVS->isEXTTextureFilterAnisotropicUsable()) + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.); } // bindTextureVolume // ---------------------------------------------------------------------------- @@ -211,7 +229,8 @@ GLuint TextureShaderBase::createSamplers(SamplerTypeNew sampler_type) glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glSamplerParameteri(id, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glSamplerParameteri(id, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glSamplerParameterf(id, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.); + if (CVS->isEXTTextureFilterAnisotropicUsable()) + glSamplerParameterf(id, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.); return createNearestSampler(); } case ST_BILINEAR_CLAMPED_FILTERED: @@ -234,7 +253,8 @@ GLuint TextureShaderBase::createNearestSampler() glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glSamplerParameteri(id, GL_TEXTURE_WRAP_S, GL_REPEAT); glSamplerParameteri(id, GL_TEXTURE_WRAP_T, GL_REPEAT); - glSamplerParameterf(id, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.); + if (CVS->isEXTTextureFilterAnisotropicUsable()) + glSamplerParameterf(id, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.); return id; #endif } // createNearestSampler @@ -250,9 +270,12 @@ GLuint TextureShaderBase::createTrilinearSampler() glSamplerParameteri(id, GL_TEXTURE_WRAP_S, GL_REPEAT); glSamplerParameteri(id, GL_TEXTURE_WRAP_T, GL_REPEAT); - int aniso = UserConfigParams::m_anisotropic; - if (aniso == 0) aniso = 1; - glSamplerParameterf(id, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)aniso); + if (CVS->isEXTTextureFilterAnisotropicUsable()) + { + int aniso = UserConfigParams::m_anisotropic; + if (aniso == 0) aniso = 1; + glSamplerParameterf(id, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)aniso); + } return id; #endif } // createTrilinearSampler @@ -267,7 +290,8 @@ GLuint TextureShaderBase::createBilinearSampler() glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glSamplerParameteri(id, GL_TEXTURE_WRAP_S, GL_REPEAT); glSamplerParameteri(id, GL_TEXTURE_WRAP_T, GL_REPEAT); - glSamplerParameterf(id, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.); + if (CVS->isEXTTextureFilterAnisotropicUsable()) + glSamplerParameterf(id, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.); return id; #endif } // createBilinearSampler @@ -297,7 +321,8 @@ GLuint TextureShaderBase::createBilinearClampedSampler() glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glSamplerParameteri(id, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glSamplerParameteri(id, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glSamplerParameterf(id, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.); + if (CVS->isEXTTextureFilterAnisotropicUsable()) + glSamplerParameterf(id, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.); return id; #endif } // createBilinearClampedSampler @@ -314,9 +339,12 @@ GLuint TextureShaderBase::createTrilinearClampedArray() glSamplerParameteri(id, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glSamplerParameteri(id, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - int aniso = UserConfigParams::m_anisotropic; - if (aniso == 0) aniso = 1; - glSamplerParameterf(id, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)aniso); + if (CVS->isEXTTextureFilterAnisotropicUsable()) + { + int aniso = UserConfigParams::m_anisotropic; + if (aniso == 0) aniso = 1; + glSamplerParameterf(id, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)aniso); + } return id; #endif } // createTrilinearClampedArray @@ -330,7 +358,8 @@ GLuint TextureShaderBase::createSemiTrilinearSampler() glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); glSamplerParameteri(id, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glSamplerParameteri(id, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glSamplerParameterf(id, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.); + if (CVS->isEXTTextureFilterAnisotropicUsable()) + glSamplerParameterf(id, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.); return id; #endif } // createSemiTrilinearSampler From 7a82a8d2c684f8a18ef432e828286033d62cb54f Mon Sep 17 00:00:00 2001 From: deve Date: Wed, 5 Oct 2016 10:56:40 +0200 Subject: [PATCH 272/350] Fixed compiler warning --- src/graphics/graphics_restrictions.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/graphics/graphics_restrictions.cpp b/src/graphics/graphics_restrictions.cpp index 9bd290e65..f18fd904a 100644 --- a/src/graphics/graphics_restrictions.cpp +++ b/src/graphics/graphics_restrictions.cpp @@ -369,6 +369,7 @@ public: // ------------- switch(m_card_test) { + case CARD_IGNORE: break; // always true case CARD_IS: if(card!=m_card_name) return false; break; From b66e6ef06df206d7533f936d39d4fc3549b6a054 Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 6 Oct 2016 00:35:52 +0800 Subject: [PATCH 273/350] Use MaxSpeed to slow down spare tire karts --- src/karts/controller/arena_ai.cpp | 3 +-- src/karts/controller/arena_ai.hpp | 1 - src/karts/controller/spare_tire_ai.cpp | 4 ++++ src/karts/controller/spare_tire_ai.hpp | 1 - 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/karts/controller/arena_ai.cpp b/src/karts/controller/arena_ai.cpp index 675028c3d..0785829ff 100644 --- a/src/karts/controller/arena_ai.cpp +++ b/src/karts/controller/arena_ai.cpp @@ -343,8 +343,7 @@ void ArenaAI::configSpeed() else { // Otherwise accelerate - m_controls->setAccel(stk_config->m_ai_acceleration * handicap * - getSpeedCap()); + m_controls->setAccel(stk_config->m_ai_acceleration * handicap); } } // configSpeed diff --git a/src/karts/controller/arena_ai.hpp b/src/karts/controller/arena_ai.hpp index 6a772155f..40adaaac5 100644 --- a/src/karts/controller/arena_ai.hpp +++ b/src/karts/controller/arena_ai.hpp @@ -128,7 +128,6 @@ private: virtual bool isWaiting() const = 0; virtual bool isKartOnRoad() const = 0; virtual void resetAfterStop() {}; - virtual float getSpeedCap() const { return 1.0f; } public: ArenaAI(AbstractKart *kart); virtual ~ArenaAI() {}; diff --git a/src/karts/controller/spare_tire_ai.cpp b/src/karts/controller/spare_tire_ai.cpp index 21f994e51..572fa6676 100644 --- a/src/karts/controller/spare_tire_ai.cpp +++ b/src/karts/controller/spare_tire_ai.cpp @@ -20,6 +20,7 @@ #include "karts/abstract_kart.hpp" #include "karts/kart_gfx.hpp" +#include "karts/max_speed.hpp" #include "modes/three_strikes_battle.hpp" #include "tracks/arena_graph.hpp" #include "tracks/arena_node.hpp" @@ -51,6 +52,9 @@ void SpareTireAI::reset() void SpareTireAI::update(float dt) { assert(!m_fixed_target_nodes.empty()); + + m_kart->setSlowdown(MaxSpeed::MS_DECREASE_AI, 0.5f, /*fade_in_time*/0.0f); + BattleAI::update(dt); m_timer -= dt; if (m_timer < 0.0f) diff --git a/src/karts/controller/spare_tire_ai.hpp b/src/karts/controller/spare_tire_ai.hpp index a8d7cb76a..e68aabb2b 100644 --- a/src/karts/controller/spare_tire_ai.hpp +++ b/src/karts/controller/spare_tire_ai.hpp @@ -35,7 +35,6 @@ private: virtual void findClosestKart(bool use_difficulty) OVERRIDE {} virtual void findTarget() OVERRIDE; - virtual float getSpeedCap() const OVERRIDE { return 0.7f; } void findDefaultPath(); public: SpareTireAI(AbstractKart *kart); From 5c4b61a26e622d40b21fe93cc1d0c9eda132baab Mon Sep 17 00:00:00 2001 From: Deve Date: Thu, 6 Oct 2016 20:31:10 +0200 Subject: [PATCH 274/350] Fixed numpad keys on linux. We need to check numlock mask, because it may be different on different systems, and possibly also modified manually. It should generally work fine, but some testing are welcome. --- .../source/Irrlicht/CIrrDeviceLinux.cpp | 55 +++++++++++++++++-- .../source/Irrlicht/CIrrDeviceLinux.h | 2 + 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp index 374d7f6f8..a8a208c7d 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp @@ -86,7 +86,7 @@ CIrrDeviceLinux::CIrrDeviceLinux(const SIrrlichtCreationParameters& param) : CIrrDeviceStub(param), #ifdef _IRR_COMPILE_WITH_X11_ display(0), visual(0), screennr(0), window(0), StdHints(0), SoftwareImage(0), - XInputMethod(0), XInputContext(0), + XInputMethod(0), XInputContext(0), numlock_mask(0), #ifdef _IRR_COMPILE_WITH_OPENGL_ glxWin(0), Context(0), @@ -140,6 +140,7 @@ CIrrDeviceLinux::CIrrDeviceLinux(const SIrrlichtCreationParameters& param) #ifdef _IRR_COMPILE_WITH_X11_ createInputContext(); + numlock_mask = getNumlockMask(display); #endif createGUIAndScene(); @@ -1260,13 +1261,59 @@ void CIrrDeviceLinux::destroyInputContext() } } +int CIrrDeviceLinux::getNumlockMask(Display* display) +{ + int mask_table[8] = {ShiftMask, LockMask, ControlMask, Mod1Mask, + Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask}; + + if (!display) + return 0; + + KeyCode numlock_keycode = XKeysymToKeycode(display, XK_Num_Lock); + + if (numlock_keycode == NoSymbol) + return 0; + + XModifierKeymap* map = XGetModifierMapping(display); + if (!map) + return 0; + + int mask = 0; + for (int i = 0; i < 8 * map->max_keypermod; i++) + { + if (map->modifiermap[i] != numlock_keycode) + continue; + + mask = mask_table[i/map->max_keypermod]; + break; + } + + XFreeModifiermap(map); + + return mask; +} + EKEY_CODE CIrrDeviceLinux::getKeyCode(XEvent &event) { EKEY_CODE keyCode = (EKEY_CODE)0; - SKeyMap mp; - mp.X11Key = XkbKeycodeToKeysym(display, event.xkey.keycode, 0, 0); - // mp.X11Key = XKeycodeToKeysym(XDisplay, event.xkey.keycode, 0); // deprecated, if we still find platforms which need that we have to use some define + + // First check for numpad keys + bool is_numpad_key = false; + if (event.xkey.state & numlock_mask) + { + mp.X11Key = XkbKeycodeToKeysym(display, event.xkey.keycode, 0, 1); + + if (mp.X11Key >=XK_KP_0 && mp.X11Key <= XK_KP_9) + is_numpad_key = true; + } + + // If it's not numpad key, then get keycode in typical way + if (!is_numpad_key) + { + mp.X11Key = XkbKeycodeToKeysym(display, event.xkey.keycode, 0, 0); + } + const s32 idx = KeyMap.binary_search(mp); if (idx != -1) { diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.h b/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.h index a50c7fa74..ea1131aaf 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.h +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.h @@ -156,6 +156,7 @@ namespace irr #ifdef _IRR_COMPILE_WITH_X11_ bool createInputContext(); void destroyInputContext(); + int getNumlockMask(Display* display); EKEY_CODE getKeyCode(XEvent &event); #endif @@ -399,6 +400,7 @@ namespace irr XIM XInputMethod; XIC XInputContext; mutable core::stringc Clipboard; + int numlock_mask; #ifdef _IRR_LINUX_X11_VIDMODE_ XF86VidModeModeInfo oldVideoMode; #endif From 91af45337f52a0aba15cd853c44d78d39c95d2d8 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 7 Oct 2016 14:39:39 +0800 Subject: [PATCH 275/350] Pre-add spare tire karts probably --- src/karts/kart.cpp | 6 +- src/modes/three_strikes_battle.cpp | 83 ++++++++++++++------------ src/modes/three_strikes_battle.hpp | 3 +- src/modes/world.cpp | 40 +++++++++++++ src/race/history.cpp | 2 +- src/race/race_manager.hpp | 6 ++ src/states_screens/race_gui.cpp | 26 +++++--- src/states_screens/race_gui.hpp | 1 + src/states_screens/race_gui_base.cpp | 16 +++-- src/states_screens/race_result_gui.cpp | 11 ++-- 10 files changed, 134 insertions(+), 60 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index ea2ef13b4..ee97c4272 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -866,13 +866,13 @@ void Kart::finishedRace(float time, bool from_server) m_finished_race = true; m_finish_time = time; - // If this is spare tire kart, end now - if (dynamic_cast(m_controller) != NULL) return; - m_controller->finishedRace(time); m_kart_model->finishedRace(); race_manager->kartFinishedRace(this, time); + // If this is spare tire kart, end now + if (dynamic_cast(m_controller) != NULL) return; + if ((race_manager->getMinorMode() == RaceManager::MINOR_MODE_NORMAL_RACE || race_manager->getMinorMode() == RaceManager::MINOR_MODE_TIME_TRIAL || race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER) diff --git a/src/modes/three_strikes_battle.cpp b/src/modes/three_strikes_battle.cpp index 15837238e..06831ad30 100644 --- a/src/modes/three_strikes_battle.cpp +++ b/src/modes/three_strikes_battle.cpp @@ -23,8 +23,7 @@ #include "graphics/camera.hpp" #include "graphics/irr_driver.hpp" #include "io/file_manager.hpp" -#include "items/item_manager.hpp" -#include "karts/kart.hpp" +#include "karts/abstract_kart.hpp" #include "karts/controller/spare_tire_ai.hpp" #include "karts/kart_model.hpp" #include "karts/kart_properties.hpp" @@ -67,42 +66,16 @@ void ThreeStrikesBattle::init() { WorldWithRank::init(); m_display_rank = false; + m_kart_info.resize(m_karts.size()); - // Spare tire karts only added with large arena - const int all_nodes = - ArenaGraph::get() ? ArenaGraph::get()->getNumNodes() : 0; - if (all_nodes > 200) + // Copy STA pointer to m_spare_tire_karts array, allowing them to respawn + // easily + for (KartList::iterator i = m_karts.begin(); i != m_karts.end() ; i++) { - const unsigned int max_sta_num = unsigned(m_karts.size() * 0.8f); - for (int i = 0; i < all_nodes; i++) - { - // Pre-spawn the spare tire karts on the item position, preventing - // affecting current karts - Item* item = ItemManager::get()->getFirstItemInQuad(i); - if (item == NULL) continue; - btTransform t; - t.setOrigin(item->getXYZ()); - t.setRotation(item->getRotation()); - - AbstractKart* sta = new Kart("nolok", m_karts.size(), - m_karts.size() + 1, t, PLAYER_DIFFICULTY_NORMAL, KRT_BLUE); - sta->init(RaceManager::KartType::KT_AI); - sta->setController(new SpareTireAI(sta)); - - m_karts.push_back(sta); - m_spare_tire_karts.push_back(sta); - m_kart_track_sector.push_back(new TrackSector()); - m_position_index.push_back(0); -#ifdef DEBUG - m_position_used.push_back(false); -#endif - m_track->adjustForFog(sta->getNode()); - - if (m_spare_tire_karts.size() >= max_sta_num) break; - } + if (dynamic_cast((*i)->getController()) != NULL) + m_spare_tire_karts.push_back(*i); } - m_kart_info.resize(m_karts.size()); } // ThreeStrikesBattle //----------------------------------------------------------------------------- @@ -137,9 +110,8 @@ void ThreeStrikesBattle::reset() bool is_sta = false; if (dynamic_cast(m_karts[n]->getController()) != NULL) { - m_kart_info[n].m_lives = -1; + m_kart_info[n].m_lives = 0; m_karts[n]->setPosition(-1); - m_karts[n]->finishedRace(0.0f); eliminateKart(n, /*notify_of_elimination*/ false); is_sta = true; } @@ -185,6 +157,16 @@ void ThreeStrikesBattle::reset() m_track->getTrackObjectManager()->removeObject(obj); } m_tires.clearWithoutDeleting(); + + // Finish all spare tire karts first + if (!m_spare_tire_karts.empty()) + { + updateKartRanks(); + for (unsigned int i = 0; i < m_spare_tire_karts.size(); i++) + { + m_spare_tire_karts[i]->finishedRace(0.0f); + } + } } // reset //----------------------------------------------------------------------------- @@ -539,7 +521,7 @@ bool ThreeStrikesBattle::isRaceOver() return (irr_driver->getRealTime()-m_start_time)*0.001f > 20.0f; // for tests : never over when we have a single player there :) - if (race_manager->getNumberOfKarts()==1 && + if (race_manager->getNumberOfKarts() - m_spare_tire_karts.size () ==1 && getCurrentNumKarts()==1 && UserConfigParams::m_artist_debug_mode) { @@ -569,8 +551,6 @@ void ThreeStrikesBattle::getKartsDisplayInfo( const unsigned int kart_amount = getNumKarts(); for(unsigned int i = 0; i < kart_amount ; i++) { - if (dynamic_cast(m_karts[i]->getController()) != NULL) - continue; RaceGUIBase::KartIconDisplayInfo& rank_info = (*info)[i]; // reset color @@ -638,3 +618,28 @@ bool ThreeStrikesBattle::spareTireKartsSpawned() const return sta->needUpdate(); } // spareTireKartsSpawned + +//----------------------------------------------------------------------------- +void ThreeStrikesBattle::addKartLife(unsigned int id) +{ + m_kart_info[id].m_lives++; + updateKartRanks(); + + scene::ISceneNode* kart_node = m_karts[id]->getNode(); + core::list& children = + const_cast&>(kart_node->getChildren()); + for (core::list::Iterator it = children.begin(); + it != children.end(); it++) + { + scene::ISceneNode* curr = *it; + if (core::stringc(curr->getName()) == "tire1") + { + curr->setVisible(m_kart_info[id].m_lives >= 3); + } + else if (core::stringc(curr->getName()) == "tire2") + { + curr->setVisible(m_kart_info[id].m_lives >= 2); + } + } + +} // addKartLife diff --git a/src/modes/three_strikes_battle.hpp b/src/modes/three_strikes_battle.hpp index 2f839f465..b9301a72a 100644 --- a/src/modes/three_strikes_battle.hpp +++ b/src/modes/three_strikes_battle.hpp @@ -118,9 +118,10 @@ public: void updateKartRanks(); void increaseRescueCount() { m_total_rescue++; } - void addKartLife(unsigned int id) { m_kart_info[id].m_lives++; } + void addKartLife(unsigned int id); int getKartLife(unsigned int id) const { return m_kart_info[id].m_lives; } bool spareTireKartsSpawned() const; + unsigned int getNumSpareTireKarts() const { return m_spare_tire_karts.size(); } }; // ThreeStrikesBattles diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 592f3914b..86410b2e1 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -32,6 +32,7 @@ #include "io/file_manager.hpp" #include "input/device_manager.hpp" #include "input/keyboard_device.hpp" +#include "items/item_manager.hpp" #include "items/projectile_manager.hpp" #include "karts/controller/battle_ai.hpp" #include "karts/controller/soccer_ai.hpp" @@ -64,6 +65,7 @@ #include "states_screens/race_gui.hpp" #include "states_screens/race_result_gui.hpp" #include "states_screens/state_manager.hpp" +#include "tracks/arena_graph.hpp" #include "tracks/track.hpp" #include "tracks/track_manager.hpp" #include "utils/constants.hpp" @@ -221,6 +223,44 @@ void World::init() } // for i + // Pre-add spare tire karts in battle mode + if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_3_STRIKES && + m_track->hasNavMesh()) + { + // Spare tire karts only added with large arena + const int all_nodes = + ArenaGraph::get() ? ArenaGraph::get()->getNumNodes() : 0; + if (all_nodes > 200) + { + const unsigned int max_sta_num = unsigned(m_karts.size() * 0.8f); + unsigned int sta_created = 0; + for (int i = 0; i < all_nodes; i++) + { + // Pre-spawn the spare tire karts on the item position, + // preven affecting current karts + Item* item = ItemManager::get()->getFirstItemInQuad(i); + if (item == NULL) continue; + btTransform t; + t.setOrigin(item->getXYZ()); + t.setRotation(item->getRotation()); + + AbstractKart* sta = new Kart("nolok", m_karts.size(), + m_karts.size() + 1, t, PLAYER_DIFFICULTY_NORMAL, KRT_BLUE); + sta->init(RaceManager::KartType::KT_AI); + sta->setController(new SpareTireAI(sta)); + + m_karts.push_back(sta); + race_manager->addSpareTireKartStatus(); + m_track->adjustForFog(sta->getNode()); + + sta_created++; + if (sta_created >= max_sta_num) break; + } + num_karts = m_karts.size(); + race_manager->setNumKarts(num_karts); + } + } + // Now that all models are loaded, apply the overrides irr_driver->applyObjectPassShader(); diff --git a/src/race/history.cpp b/src/race/history.cpp index 30ebbcbd4..85ce6492a 100644 --- a/src/race/history.cpp +++ b/src/race/history.cpp @@ -96,7 +96,7 @@ void History::updateSaving(float dt) m_all_deltas[m_current] = dt; World *world = World::getWorld(); - unsigned int num_karts = race_manager->getNumberOfKarts(); + unsigned int num_karts = world->getNumKarts(); unsigned int index = m_current*num_karts; for(unsigned int i=0; igetDimension(L"9/9").Width; +} // RaceGUI + +//----------------------------------------------------------------------------- +RaceGUI::~RaceGUI() +{ +} // ~Racegui + + +//----------------------------------------------------------------------------- +void RaceGUI::init() +{ + RaceGUIBase::init(); // Technically we only need getNumLocalPlayers, but using the // global kart id to find the data for a specific kart. int n = race_manager->getNumberOfKarts(); @@ -111,12 +123,7 @@ RaceGUI::RaceGUI() m_animation_states.resize(n); m_rank_animation_duration.resize(n); m_last_ranks.resize(n); -} // RaceGUI - -//----------------------------------------------------------------------------- -RaceGUI::~RaceGUI() -{ -} // ~Racegui +} // init //----------------------------------------------------------------------------- /** Reset the gui before a race. It initialised all rank animation related @@ -369,7 +376,10 @@ void RaceGUI::drawGlobalMiniMap() for(unsigned int i=0; igetNumKarts(); i++) { const AbstractKart *kart = world->getKart(i); - if(kart->isEliminated()) continue; // don't draw eliminated kart + const SpareTireAI* sta = + dynamic_cast(kart->getController()); + // don't draw eliminated kart + if(kart->isEliminated() && !(sta && sta->needUpdate())) continue; const Vec3& xyz = kart->getXYZ(); Vec3 draw_at; world->getTrack()->mapPoint2MiniMap(xyz, &draw_at); diff --git a/src/states_screens/race_gui.hpp b/src/states_screens/race_gui.hpp index a7f01aed4..93e7e31e3 100644 --- a/src/states_screens/race_gui.hpp +++ b/src/states_screens/race_gui.hpp @@ -119,6 +119,7 @@ public: RaceGUI(); ~RaceGUI(); + virtual void init(); virtual void reset(); virtual void renderGlobal(float dt); virtual void renderPlayerView(const Camera *camera, float dt); diff --git a/src/states_screens/race_gui_base.cpp b/src/states_screens/race_gui_base.cpp index 492fe0587..2428c33b2 100644 --- a/src/states_screens/race_gui_base.cpp +++ b/src/states_screens/race_gui_base.cpp @@ -42,7 +42,7 @@ #include "karts/kart_properties_manager.hpp" #include "karts/rescue_animation.hpp" #include "modes/linear_world.hpp" -#include "modes/world.hpp" +#include "modes/three_strikes_battle.hpp" #include "tracks/track.hpp" #include "utils/constants.hpp" @@ -638,8 +638,15 @@ void RaceGUIBase::drawGlobalPlayerIcons(int bottom_margin) y_space = irr_driver->getActualScreenSize().Height - y_base; } + unsigned int sta = 0; + ThreeStrikesBattle* tsb = dynamic_cast(World::getWorld()); + if (tsb) + sta = tsb->getNumSpareTireKarts(); + + const unsigned int num_karts = race_manager->getNumberOfKarts() - sta; + // -2 because that's the spacing further on - int ICON_PLAYER_WIDTH = y_space / race_manager->getNumberOfKarts() - 2; + int ICON_PLAYER_WIDTH = y_space / num_karts - 2; int icon_width_max = (int)(50*(irr_driver->getActualScreenSize().Width/800.0f)); int icon_width_min = (int)(35*(irr_driver->getActualScreenSize().Height/600.0f)); @@ -664,10 +671,11 @@ void RaceGUIBase::drawGlobalPlayerIcons(int bottom_margin) int ICON_WIDTH = ICON_PLAYER_WIDTH * 4 / 5; WorldWithRank *world = (WorldWithRank*)(World::getWorld()); + //initialize m_previous_icons_position if(m_previous_icons_position.size()==0) { - for(unsigned int i=0; igetNumberOfKarts(); i++) + for(unsigned int i=0; igetKart(i); int position = kart->getPosition(); @@ -686,7 +694,7 @@ void RaceGUIBase::drawGlobalPlayerIcons(int bottom_margin) int previous_y=y_base-ICON_PLAYER_WIDTH-2; gui::ScalableFont* font = GUIEngine::getFont(); - const unsigned int kart_amount = world->getNumKarts(); + const unsigned int kart_amount = world->getNumKarts() - sta; //where is the limit to hide last icons int y_icons_limit=irr_driver->getActualScreenSize().Height-bottom_margin-ICON_PLAYER_WIDTH; diff --git a/src/states_screens/race_result_gui.cpp b/src/states_screens/race_result_gui.cpp index 714f2987c..972c8dab3 100644 --- a/src/states_screens/race_result_gui.cpp +++ b/src/states_screens/race_result_gui.cpp @@ -43,7 +43,7 @@ #include "modes/demo_world.hpp" #include "modes/overworld.hpp" #include "modes/soccer_world.hpp" -#include "modes/world_with_rank.hpp" +#include "modes/three_strikes_battle.hpp" #include "network/protocol_manager.hpp" #include "network/protocols/client_lobby_room_protocol.hpp" #include "race/highscores.hpp" @@ -460,17 +460,20 @@ void RaceResultGUI::backToLobby() WorldWithRank *rank_world = (WorldWithRank*)World::getWorld(); unsigned int first_position = 1; + unsigned int sta = 0; if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER) first_position = 2; + ThreeStrikesBattle* tsb = dynamic_cast(World::getWorld()); + if (tsb) + sta = tsb->getNumSpareTireKarts(); // Use only the karts that are supposed to be displayed (and // ignore e.g. the leader in a FTL race). - unsigned int num_karts = race_manager->getNumberOfKarts() - first_position + 1; + unsigned int num_karts = race_manager->getNumberOfKarts() - first_position + 1 - sta; // In FTL races the leader kart is not displayed m_all_row_infos.resize(num_karts); - // Determine the kart to display in the right order, // and the maximum width for the kart name column // ------------------------------------------------- @@ -478,7 +481,7 @@ void RaceResultGUI::backToLobby() float max_finish_time = 0; for (unsigned int position = first_position; - position <= race_manager->getNumberOfKarts(); position++) + position <= race_manager->getNumberOfKarts() - sta; position++) { const AbstractKart *kart = rank_world->getKartAtPosition(position); From b055fa573d17e2177fddb00b0ca19a2ff1aedcbe Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 7 Oct 2016 15:29:36 +0800 Subject: [PATCH 276/350] Try to fix hangs when firstly spawn STA --- src/karts/controller/spare_tire_ai.cpp | 6 +++--- src/modes/three_strikes_battle.cpp | 23 ++++++++++++++++++----- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/karts/controller/spare_tire_ai.cpp b/src/karts/controller/spare_tire_ai.cpp index 572fa6676..37a5a2dab 100644 --- a/src/karts/controller/spare_tire_ai.cpp +++ b/src/karts/controller/spare_tire_ai.cpp @@ -51,14 +51,14 @@ void SpareTireAI::reset() //----------------------------------------------------------------------------- void SpareTireAI::update(float dt) { - assert(!m_fixed_target_nodes.empty()); + if (m_fixed_target_nodes.empty()) return; m_kart->setSlowdown(MaxSpeed::MS_DECREASE_AI, 0.5f, /*fade_in_time*/0.0f); - - BattleAI::update(dt); m_timer -= dt; if (m_timer < 0.0f) unspawn(); + + BattleAI::update(dt); } // update //----------------------------------------------------------------------------- diff --git a/src/modes/three_strikes_battle.cpp b/src/modes/three_strikes_battle.cpp index 06831ad30..8feeda837 100644 --- a/src/modes/three_strikes_battle.cpp +++ b/src/modes/three_strikes_battle.cpp @@ -106,22 +106,21 @@ void ThreeStrikesBattle::reset() for(unsigned int n=0; n(m_karts[n]->getController()) != NULL) { + // STA has no life m_kart_info[n].m_lives = 0; - m_karts[n]->setPosition(-1); - eliminateKart(n, /*notify_of_elimination*/ false); is_sta = true; } else { m_kart_info[n].m_lives = 3; - // no positions in this mode - m_karts[n]->setPosition(-1); } + // no positions in this mode + m_karts[n]->setPosition(-1); + scene::ISceneNode* kart_node = m_karts[n]->getNode(); // FIXME: sorry for this ugly const_cast, irrlicht doesn't seem to allow getting a writable list of children, wtf?? @@ -165,6 +164,8 @@ void ThreeStrikesBattle::reset() for (unsigned int i = 0; i < m_spare_tire_karts.size(); i++) { m_spare_tire_karts[i]->finishedRace(0.0f); + m_spare_tire_karts[i]->getNode()->setVisible(false); + m_eliminated_karts++; } } } // reset @@ -349,6 +350,18 @@ void ThreeStrikesBattle::update(float dt) WorldWithRank::update(dt); WorldWithRank::updateTrack(dt); + if (getPhase() == World::GO_PHASE) + { + // Eliminate all spare tire karts first, they will be spawned if needed + for (unsigned int i = 0; i < m_spare_tire_karts.size(); i++) + { + if (!m_spare_tire_karts[i]->isEliminated()) + { + m_spare_tire_karts[i]->eliminate(); + } + } + } + const float period = 20.0f; if (!m_spare_tire_karts.empty() && period < getTimeSinceStart() / float(m_sta_spawned_count)) From 2147b74fe4e57bae367b3543e62f94dc46e84e51 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 7 Oct 2016 16:03:15 +0800 Subject: [PATCH 277/350] Fix engine sound after re-spawn --- src/karts/controller/spare_tire_ai.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/karts/controller/spare_tire_ai.cpp b/src/karts/controller/spare_tire_ai.cpp index 37a5a2dab..6ecebd9ef 100644 --- a/src/karts/controller/spare_tire_ai.cpp +++ b/src/karts/controller/spare_tire_ai.cpp @@ -110,6 +110,7 @@ void SpareTireAI::spawn(float time_to_last) m_timer = time_to_last; World::getWorld()->getPhysics()->addKart(m_kart); + m_kart->startEngineSFX(); m_kart->getKartGFX()->reset(); m_kart->getNode()->setVisible(true); From c907442bb489e3b0b06a655f6922f952e5f7100c Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 7 Oct 2016 16:43:54 +0800 Subject: [PATCH 278/350] Fix incorrect target node access --- src/karts/controller/spare_tire_ai.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/karts/controller/spare_tire_ai.cpp b/src/karts/controller/spare_tire_ai.cpp index 6ecebd9ef..91dc8ee18 100644 --- a/src/karts/controller/spare_tire_ai.cpp +++ b/src/karts/controller/spare_tire_ai.cpp @@ -51,14 +51,11 @@ void SpareTireAI::reset() //----------------------------------------------------------------------------- void SpareTireAI::update(float dt) { - if (m_fixed_target_nodes.empty()) return; - + BattleAI::update(dt); m_kart->setSlowdown(MaxSpeed::MS_DECREASE_AI, 0.5f, /*fade_in_time*/0.0f); m_timer -= dt; if (m_timer < 0.0f) unspawn(); - - BattleAI::update(dt); } // update //----------------------------------------------------------------------------- @@ -95,6 +92,9 @@ void SpareTireAI::findDefaultPath() //----------------------------------------------------------------------------- void SpareTireAI::findTarget() { + if (m_fixed_target_nodes.empty()) return; + + assert(m_fixed_target_nodes.size() == 3); if (getCurrentNode() == m_fixed_target_nodes[m_idx]) m_idx = m_idx == 2 ? 0 : m_idx + 1; From 71662df159ff278333cad1caed49d5197135f67e Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 7 Oct 2016 21:49:05 +0800 Subject: [PATCH 279/350] Improve spawn formula --- src/modes/three_strikes_battle.cpp | 87 +++++++++++++++++++----------- src/modes/three_strikes_battle.hpp | 3 +- 2 files changed, 57 insertions(+), 33 deletions(-) diff --git a/src/modes/three_strikes_battle.cpp b/src/modes/three_strikes_battle.cpp index 8feeda837..254024c0f 100644 --- a/src/modes/three_strikes_battle.cpp +++ b/src/modes/three_strikes_battle.cpp @@ -101,7 +101,12 @@ void ThreeStrikesBattle::reset() { WorldWithRank::reset(); - m_sta_spawned_count = 1; + m_next_sta_spawn_time = + race_manager->getDifficulty() == RaceManager::DIFFICULTY_BEST ? 40.0f : + race_manager->getDifficulty() == RaceManager::DIFFICULTY_HARD ? 30.0f : + race_manager->getDifficulty() == RaceManager::DIFFICULTY_MEDIUM ? + 25.0f : 20.0f; + const unsigned int kart_amount = (unsigned int)m_karts.size(); for(unsigned int n=0; n 1.0f) - { - unsigned int spawn_sta = unsigned(ratio); - if (spawn_sta > m_spare_tire_karts.size()) - spawn_sta = m_spare_tire_karts.size(); - m_race_gui->addMessage(_P("%i spare tire kart has been spawned!", - "%i spare tire karts have been spawned!", - spawn_sta), NULL, 2.0f); - for (unsigned int i = 0; i < spawn_sta; i++) - { - SpareTireAI* sta = dynamic_cast - (m_spare_tire_karts[i]->getController()); - assert(sta); - sta->spawn(period / 2); - } - } - } - + spawnSpareTireKarts(); if (m_track->hasNavMesh()) updateSectorForKarts(); @@ -656,3 +631,51 @@ void ThreeStrikesBattle::addKartLife(unsigned int id) } } // addKartLife + +//----------------------------------------------------------------------------- +void ThreeStrikesBattle::spawnSpareTireKarts() +{ + if (m_spare_tire_karts.empty() || + getTimeSinceStart() < m_next_sta_spawn_time) + return; + + const float period = + race_manager->getDifficulty() == RaceManager::DIFFICULTY_BEST ? 40.0f : + race_manager->getDifficulty() == RaceManager::DIFFICULTY_HARD ? 30.0f : + race_manager->getDifficulty() == RaceManager::DIFFICULTY_MEDIUM ? + 25.0f : 20.0f; + const float inc_factor = + race_manager->getDifficulty() == RaceManager::DIFFICULTY_BEST ? 0.7f : + race_manager->getDifficulty() == RaceManager::DIFFICULTY_HARD ? 0.65f : + race_manager->getDifficulty() == RaceManager::DIFFICULTY_MEDIUM ? + 0.6f : 0.55f; + + // Spawn spare tire kart when necessary + // The lifespan for sta: inc_factor / period * 1000 / 2 + // So in easier mode the sta lasts longer than spawn period + const float lifespan = inc_factor / period * 1000; + m_next_sta_spawn_time = lifespan + (getTimeSinceStart() * inc_factor) + + getTimeSinceStart(); + int kart_has_few_lives = 0; + for (unsigned int i = 0; i < m_kart_info.size(); i++) + { + if (m_kart_info[i].m_lives > 0 && m_kart_info[i].m_lives < 3) + kart_has_few_lives++; + } + + float ratio = kart_has_few_lives / (inc_factor * 2); + if (ratio < 1.5f) return; + unsigned int spawn_sta = unsigned(ratio); + if (spawn_sta > m_spare_tire_karts.size()) + spawn_sta = m_spare_tire_karts.size(); + m_race_gui->addMessage(_P("%i spare tire kart has been spawned!", + "%i spare tire karts have been spawned!", + spawn_sta), NULL, 2.0f); + for (unsigned int i = 0; i < spawn_sta; i++) + { + SpareTireAI* sta = dynamic_cast + (m_spare_tire_karts[i]->getController()); + assert(sta); + sta->spawn(lifespan); + } +} // spawnSpareTireKarts diff --git a/src/modes/three_strikes_battle.hpp b/src/modes/three_strikes_battle.hpp index b9301a72a..77c826d66 100644 --- a/src/modes/three_strikes_battle.hpp +++ b/src/modes/three_strikes_battle.hpp @@ -78,7 +78,7 @@ private: int m_total_hit; std::vector m_spare_tire_karts; - int m_sta_spawned_count; + float m_next_sta_spawn_time; public: @@ -121,6 +121,7 @@ public: void addKartLife(unsigned int id); int getKartLife(unsigned int id) const { return m_kart_info[id].m_lives; } bool spareTireKartsSpawned() const; + void spawnSpareTireKarts(); unsigned int getNumSpareTireKarts() const { return m_spare_tire_karts.size(); } }; // ThreeStrikesBattles From 59edb96489e4388b040cd4c1e51ecf57097382f7 Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 8 Oct 2016 08:35:07 +0800 Subject: [PATCH 280/350] Fix warning when eliminate a kart in battle mode --- src/graphics/texture_manager.cpp | 4 ++-- src/tracks/track_object_presentation.cpp | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/graphics/texture_manager.cpp b/src/graphics/texture_manager.cpp index 5b823cf72..fce9c61f7 100644 --- a/src/graphics/texture_manager.cpp +++ b/src/graphics/texture_manager.cpp @@ -34,8 +34,8 @@ GLuint getTextureGLuint(irr::video::ITexture *tex) { - if (tex == NULL) - return 0; + if (tex == NULL) + return 0; #if defined(USE_GLES2) return static_cast(tex)->getOpenGLTextureName(); #else diff --git a/src/tracks/track_object_presentation.cpp b/src/tracks/track_object_presentation.cpp index 783ba803f..eebfa6ad4 100644 --- a/src/tracks/track_object_presentation.cpp +++ b/src/tracks/track_object_presentation.cpp @@ -405,7 +405,7 @@ TrackObjectPresentationMesh::TrackObjectPresentationMesh( World::getWorld()->getIdent() == IDENT_CUTSCENE); m_model_file = model_file; - + file_manager->pushTextureSearchPath(StringUtils::getPath(model_file)); if (file_manager->fileExists(model_file)) { if (animated) @@ -419,6 +419,7 @@ TrackObjectPresentationMesh::TrackObjectPresentationMesh( throw std::runtime_error("Model '" + model_file + "' cannot be found"); } + file_manager->popTextureSearchPath(); init(NULL, NULL, true); } // TrackObjectPresentationMesh From 8889654d962a7d5918adf87f8742abba60c4cd3a Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 8 Oct 2016 09:41:02 +0800 Subject: [PATCH 281/350] Move STA function out of world --- src/karts/kart.cpp | 1 - src/modes/three_strikes_battle.cpp | 56 ++++++++++++++++++++++++------ src/modes/three_strikes_battle.hpp | 48 ++++++++++++++++--------- src/modes/world.cpp | 43 ++--------------------- src/modes/world.hpp | 2 ++ 5 files changed, 81 insertions(+), 69 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index ee97c4272..7aca75ccf 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -865,7 +865,6 @@ void Kart::finishedRace(float time, bool from_server) */ m_finished_race = true; m_finish_time = time; - m_controller->finishedRace(time); m_kart_model->finishedRace(); race_manager->kartFinishedRace(this, time); diff --git a/src/modes/three_strikes_battle.cpp b/src/modes/three_strikes_battle.cpp index 254024c0f..96967e881 100644 --- a/src/modes/three_strikes_battle.cpp +++ b/src/modes/three_strikes_battle.cpp @@ -23,7 +23,8 @@ #include "graphics/camera.hpp" #include "graphics/irr_driver.hpp" #include "io/file_manager.hpp" -#include "karts/abstract_kart.hpp" +#include "items/item_manager.hpp" +#include "karts/kart.hpp" #include "karts/controller/spare_tire_ai.hpp" #include "karts/kart_model.hpp" #include "karts/kart_properties.hpp" @@ -32,7 +33,6 @@ #include "tracks/arena_graph.hpp" #include "tracks/track.hpp" #include "tracks/track_object_manager.hpp" -#include "tracks/track_sector.hpp" #include "utils/constants.hpp" #include @@ -67,15 +67,6 @@ void ThreeStrikesBattle::init() WorldWithRank::init(); m_display_rank = false; m_kart_info.resize(m_karts.size()); - - // Copy STA pointer to m_spare_tire_karts array, allowing them to respawn - // easily - for (KartList::iterator i = m_karts.begin(); i != m_karts.end() ; i++) - { - if (dynamic_cast((*i)->getController()) != NULL) - m_spare_tire_karts.push_back(*i); - } - } // ThreeStrikesBattle //----------------------------------------------------------------------------- @@ -679,3 +670,46 @@ void ThreeStrikesBattle::spawnSpareTireKarts() sta->spawn(lifespan); } } // spawnSpareTireKarts + +//----------------------------------------------------------------------------- +void ThreeStrikesBattle::loadCustomModels() +{ + // Pre-add spare tire karts + if (ArenaGraph::get()) + { + // Spare tire karts only added with large arena + const int all_nodes = ArenaGraph::get()->getNumNodes(); + if (all_nodes > 200) + { + const unsigned int max_sta_num = unsigned(m_karts.size() * 0.8f); + unsigned int sta_created = 0; + for (int i = 0; i < all_nodes; i++) + { + // Pre-spawn the spare tire karts on the item position, + // preven affecting current karts + Item* item = ItemManager::get()->getFirstItemInQuad(i); + if (item == NULL) continue; + btTransform t; + t.setOrigin(item->getXYZ()); + t.setRotation(item->getRotation()); + + AbstractKart* sta = new Kart("nolok", m_karts.size(), + m_karts.size() + 1, t, PLAYER_DIFFICULTY_NORMAL, KRT_RED); + sta->init(RaceManager::KartType::KT_AI); + sta->setController(new SpareTireAI(sta)); + + m_karts.push_back(sta); + race_manager->addSpareTireKartStatus(); + m_track->adjustForFog(sta->getNode()); + + // Copy STA pointer to m_spare_tire_karts array, allowing them + // to respawn easily + m_spare_tire_karts.push_back(sta); + + sta_created++; + if (sta_created >= max_sta_num) break; + } + race_manager->setNumKarts(m_karts.size()); + } + } +} // loadCustomModels diff --git a/src/modes/three_strikes_battle.hpp b/src/modes/three_strikes_battle.hpp index 77c826d66..5f0a700af 100644 --- a/src/modes/three_strikes_battle.hpp +++ b/src/modes/three_strikes_battle.hpp @@ -81,48 +81,62 @@ private: float m_next_sta_spawn_time; public: - /** Used to show a nice graph when battle is over */ struct BattleEvent { float m_time; std::vector m_kart_info; }; + // ------------------------------------------------------------------------ std::vector m_battle_events; - + // ------------------------------------------------------------------------ ThreeStrikesBattle(); + // ------------------------------------------------------------------------ virtual ~ThreeStrikesBattle(); - + // ------------------------------------------------------------------------ virtual void init() OVERRIDE; - + // ------------------------------------------------------------------------ // clock events virtual bool isRaceOver() OVERRIDE; + // ------------------------------------------------------------------------ virtual void terminateRace() OVERRIDE; - + // ------------------------------------------------------------------------ // overriding World methods virtual void reset() OVERRIDE; - - //virtual void getDefaultCollectibles(int& collectible_type, int& amount); - virtual bool useFastMusicNearEnd() const OVERRIDE { return false; } + // ------------------------------------------------------------------------ virtual void getKartsDisplayInfo( std::vector *info) OVERRIDE; - virtual bool raceHasLaps() OVERRIDE { return false; } - + // ------------------------------------------------------------------------ + virtual bool raceHasLaps() OVERRIDE { return false; } + // ------------------------------------------------------------------------ virtual const std::string& getIdent() const OVERRIDE; - + // ------------------------------------------------------------------------ virtual void kartHit(const unsigned int kart_id) OVERRIDE; + // ------------------------------------------------------------------------ virtual void update(float dt) OVERRIDE; - - virtual void kartAdded(AbstractKart* kart, scene::ISceneNode* node) OVERRIDE; + // ------------------------------------------------------------------------ + virtual void kartAdded(AbstractKart* kart, scene::ISceneNode* node) + OVERRIDE; + // ------------------------------------------------------------------------ virtual void enterRaceOverState() OVERRIDE; - + // ------------------------------------------------------------------------ + virtual void loadCustomModels() OVERRIDE; + // ------------------------------------------------------------------------ void updateKartRanks(); - void increaseRescueCount() { m_total_rescue++; } + // ------------------------------------------------------------------------ + void increaseRescueCount() { m_total_rescue++; } + // ------------------------------------------------------------------------ void addKartLife(unsigned int id); - int getKartLife(unsigned int id) const { return m_kart_info[id].m_lives; } + // ------------------------------------------------------------------------ + int getKartLife(unsigned int id) const { return m_kart_info[id].m_lives; } + // ------------------------------------------------------------------------ bool spareTireKartsSpawned() const; + // ------------------------------------------------------------------------ void spawnSpareTireKarts(); - unsigned int getNumSpareTireKarts() const { return m_spare_tire_karts.size(); } + // ------------------------------------------------------------------------ + unsigned int getNumSpareTireKarts() const + { return m_spare_tire_karts.size(); } + }; // ThreeStrikesBattles diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 86410b2e1..3b14eae12 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -32,7 +32,6 @@ #include "io/file_manager.hpp" #include "input/device_manager.hpp" #include "input/keyboard_device.hpp" -#include "items/item_manager.hpp" #include "items/projectile_manager.hpp" #include "karts/controller/battle_ai.hpp" #include "karts/controller/soccer_ai.hpp" @@ -65,7 +64,6 @@ #include "states_screens/race_gui.hpp" #include "states_screens/race_result_gui.hpp" #include "states_screens/state_manager.hpp" -#include "tracks/arena_graph.hpp" #include "tracks/track.hpp" #include "tracks/track_manager.hpp" #include "utils/constants.hpp" @@ -223,43 +221,8 @@ void World::init() } // for i - // Pre-add spare tire karts in battle mode - if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_3_STRIKES && - m_track->hasNavMesh()) - { - // Spare tire karts only added with large arena - const int all_nodes = - ArenaGraph::get() ? ArenaGraph::get()->getNumNodes() : 0; - if (all_nodes > 200) - { - const unsigned int max_sta_num = unsigned(m_karts.size() * 0.8f); - unsigned int sta_created = 0; - for (int i = 0; i < all_nodes; i++) - { - // Pre-spawn the spare tire karts on the item position, - // preven affecting current karts - Item* item = ItemManager::get()->getFirstItemInQuad(i); - if (item == NULL) continue; - btTransform t; - t.setOrigin(item->getXYZ()); - t.setRotation(item->getRotation()); - - AbstractKart* sta = new Kart("nolok", m_karts.size(), - m_karts.size() + 1, t, PLAYER_DIFFICULTY_NORMAL, KRT_BLUE); - sta->init(RaceManager::KartType::KT_AI); - sta->setController(new SpareTireAI(sta)); - - m_karts.push_back(sta); - race_manager->addSpareTireKartStatus(); - m_track->adjustForFog(sta->getNode()); - - sta_created++; - if (sta_created >= max_sta_num) break; - } - num_karts = m_karts.size(); - race_manager->setNumKarts(num_karts); - } - } + // Load other custom models if needed + loadCustomModels(); // Now that all models are loaded, apply the overrides irr_driver->applyObjectPassShader(); @@ -267,7 +230,7 @@ void World::init() // Must be called after all karts are created m_race_gui->init(); - powerup_manager->updateWeightsForRace(num_karts); + powerup_manager->updateWeightsForRace(race_manager->getNumberOfKarts()); if (UserConfigParams::m_weather_effects) { diff --git a/src/modes/world.hpp b/src/modes/world.hpp index f74e7c6cd..fedeb4e79 100644 --- a/src/modes/world.hpp +++ b/src/modes/world.hpp @@ -360,6 +360,8 @@ public: void delayedSelfDestruct(); // ------------------------------------------------------------------------ virtual void escapePressed(); + // ------------------------------------------------------------------------ + virtual void loadCustomModels() {} /** Set the network mode (true if networked) */ void setNetworkWorld(bool is_networked) { m_is_network_world = is_networked; } From 71359a087dcb8c83a9effa9f79dbcb3f35fcb90e Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 8 Oct 2016 14:29:28 +0800 Subject: [PATCH 282/350] Add heart billboard with red kart for spare tire karts --- data/gui/heart.png | Bin 0 -> 8736 bytes src/karts/kart_gfx.cpp | 3 +- src/karts/kart_properties_manager.cpp | 26 ++++++----- src/karts/kart_properties_manager.hpp | 2 +- src/modes/soccer_world.cpp | 11 ++--- src/modes/three_strikes_battle.cpp | 61 ++++++++++++++++---------- src/modes/world.cpp | 2 + src/race/race_manager.cpp | 2 +- src/race/race_manager.hpp | 11 ++--- src/states_screens/race_gui.cpp | 4 +- 10 files changed, 73 insertions(+), 49 deletions(-) create mode 100644 data/gui/heart.png diff --git a/data/gui/heart.png b/data/gui/heart.png new file mode 100644 index 0000000000000000000000000000000000000000..bc4dff5365384e997fa1be52bc90f00c2d5e0531 GIT binary patch literal 8736 zcmV+*BH!JKP)^zu5-qk|WhIm&$#InuMM*`GO5T*%@)A2L$#z7^id~8AEOyC~1rW4E(c&T! z1R#Q-NG!xM0A>cj>@)9m=Z{|Axu@^#<;@!qWN@nb-M#zv{l0VV?bFMH%W_#R%VoJN z|DTF^xjP``5!lp(?GfCNY}Xs;1%`l`2&Mvf5%^vRPmaUl%iQHm_CwzU+}?m4A>0UT zHn1rvo{8WXa2Pn;3oqOaM_;zi3VB%xup34q*crgvfqQ}5YV>Ilc*MXTh49DYFuP_Q z^~hgB{~-KB1MdcQ0>d?$9|gV1Iqr)5u z;Gf+C-&v#m^Dx{J!h^t@*60wEz!!VqGk3#@H9AHnYfgaOv4(%Zz`p^u7IkEl2J7fy zkbr&y1~4=*1V9s5A!MGA8Csm9$pWEkS54rfWALB5eJ*?8y%GE_FsRG=3D`&v>u4}U zKpz1;7!0sN#1bKk$u>ia^R#FY6&-E?_}yOkmAAp^Zl6mkYf6AU@SX@h4P4_Y@4>Kz zUap{r4fN2c*_{itIK~oZS#cGA{xp2xUC^rEb6X-X564r?zt&~z>0ulFY^0~|KDj`X zX_`#1!lIkZ0J8=@GY7xC7E{BT5MVdlVBo(3Kk6#qKo3{a#};}By3mDbnjB<-rTid% zNCxYJ zz%Sug5pSf28yH|6jcy(mfF=z;8$J(QYkvyGDTBFk;{fz`JzWIvU)_0PAa-$+{8&&#}NMmUYpW zcEWoNemm=uJ@D%hd|2jfHhtV249C#BFxN!rkQfVuyvQPF+|%3RJ#c?Fd{{)>0HVGM_&1y1Cr%TJHNzpc8Phu|Zqh2$pF$BxE`W7>-HWRSr7 z;uU0yCWlyXtq~@HpBjU&*V>_yRpat7+!?}GfsHbMBR#yHK?bCmTnjPDDVXdBjNxE7 z$EnC7L1f_FJK@V^yW9mo7QkOXd#<`7=wnCE`s4@+h&e1Z5oB7#GZHw6H_+@Qr~W zCe-ow~>AeJgzbD#mGVanRw36z;77Cj)6_|HX6yk#j(;&=Lboi5q+}a zBCqg#+`2hTgWDNl*uIhqfG_Nd??==d-ntDEE%_USE3`KOf4EzhcP=fjY5^=u5C<2+ zCQmJ%wUpDqhj+%;_W7yk-wsD|4LnlY0?fi^5a0*Et=$4Y1zKxGrZGOvacG#($aOt^ytdC;rVzoe z?1CKtyWz?JJ}1ks=^J5l-=MAGnT2s#fHY5831^=?p%GzvZZa;U7sIV6Uj%Gzz?Y&L zxYi=TZnzKlfXsUXLyWE#d<}e{<1X`Q{NOvx?{x#CY_zqxKY#}xhh77}L;K!6!;M~U z9N1_BZ4|&h-~&Yr4R8_odI~5ZLLY`7U`V^r?|uk=q4LmIt@tRcTZH|S8?ka{GC2JXJ=3RN-X`s#&;EAin z;whFn$b#(2Vg$F3!?VRFQNsc(#y3JT@P`PvvC~UEpT=8@s0wDY&_d+neT^Qr4_%>q zW8JUVHnf4^-u|SG*i4}MZA6*Mv!;OxroYHixZ?7dz?Jmr#rHrEPu1m6RH?KFu8ZIx z;x_2*jCAJHmxE|&c})wD^8u7wfHb~;W`eWJ^SQ2#^z`wj4c8<$aZAK$0ZP1orJ7&U z3J6QE9ECi_3{BCe5bnD-zEt;0B?}P2ClKIorMEu#Ic?Wkt~9vl63TV?RKWtI@$I8q z2ppGL+ef!xf<|8ZbN%*N0J$E}adDo$u+$1T`jEu?Ou+T@YjA(nb-8Q7Qij`ZxYoc6 z#1Cf@06)ZNJr}R;xKI0y!Pam`+-CtKpw2w|bdStWad>trOK%w%;gpVl6)ULgDQge}4YrxwqWl*NAu1~T zCX6=WC171z`Od1^@6>v&(Y`#HPUA&+G8Cs(U;!|2W?_~C)5p1f6c5r5k{$8y^WL=oIJ4*OhZQFw!X1@1Qq_yAkzEeF38Y*nT~Eu02q z9SXwi#}RKwwhXOf>(UI@uixB0mQx}}H0f6cc9M>wB%b6O6X%tMX}7X{DS{d7w9Jdz zi2%4Z7~q9)Uew`hf-6qDeVS*5;YLyPDOCtIO=gJ3M{~$_|k7IO%D*P0(=kP zF3`QeS0MrI{Z+64Zu+ZC<6uB!?6REJrg3QraEEFCGbK!ZISt?_7kSpc5$$Q$Ee(s}N!k%rC+IM}De#&I zMI>;9=qaEC{4{O#$IXV<1pQ35+7FNf@J{Lw0Qd=+aNg$aMB{UWQ002RGM&ZK0jUz| zxGSy7EQuUh4&XD*G!8;2&|RABS__n|0eq&wz$VjA(3;I29*Gp5zOx135g1&KpE(2H?IS4pS|fix;)~U<#SAh zZ7X2lww>^dJT0dMScbPEJb#;XH4T=_OI1hfy+pun(-ji9(yAm$5Hj1 zq6(#nb^-4qfio1l_ko|Lo#2c86mVlh(5JKR0)9(oI0+EN79f+%@1Fx+d86u@ey65a z0NzD{3|t2ZG|)LBRFt$`&H^~%neq#;GJIWDKpqD_u&G5T06vxBMlU_a&}@_F@9H|f zMc^H2q!$xAe=_hZg6^{r=}8n3#RUFkB(Spd0u2WwTIgq;C?dcj+$^2FLo|xP@5Wjn1<(b$ z&-538?<7I?<4*e|t0GqO>2Y1x?=Fo;l~n~ljk-utQHFxt<@-&*!)YQ%fCk(o4>J@r z+~9Ww__{UalYn+RR9-4oEkIUHTL6bC_nCeRd`DaYU14=!M+ViijYi?? zS_ED#6aAV1PH?LNUzZH1Jej6lC%5uj{efMeG+!TNVd>_aTl2rb-Niy9CY1nD=wfH{I9JmQE zltzLg?Jop>6%Hflj0mf-06sEA*^dqUYJC2#bgn<*N+7KgP(lK|IBkov0F1;+?j?Y+ zB6ZAmR~<$O?e^u#GzDQ=PY!a?U|Itp##dT^G7_Z6QS*Bd_-VG!@_YN7n474mj$2#= z$W{ySIn)8a8^G5kf{NF*07hphDfIt~1)dHq@RLjr?rq?G6+}I|b9RBN+_An~5Eyqx zfHFr=UGQZ+eDzRuD2a=(C|)LM-6BVh4mJ3eb4P$&rt!JH$b?e&oc5>ID8KuDF`2js zkiBNwN?F^9_OHreq>ky*ehIP#xFP|lay_6$WRUZ?Ac8UNvK;t$tbkSEum&aCp8>42 z-YS#*Hd&tfrVtJBjX@Uy&ZUuN6jsw`*rxmyM&VG2w-43?Mf?(h>XG12CZA**C7J!daiXqT5b)f6>*5R z03NM(S^*K4hwA9k-oCES?K7>DTL{MeT?wtv$V|(G>t>KuVI>Ob3-k>k{6N+97OvJy z2#$C%GENdW22zi!)Y??@v`No_k4=_JuHN+EK34cI?Xu#`P3;0dZl^woF(nf&3-FTc z&QjE(&-AVyz;3nEpj&_!fUh;a$O5FGXMrSwBmp8!5*p8x47D`h2db)KM4?_IB;dEA zkfv<`j>$xh03jTa$6JhAjIPFXmo5;icnk68=YTIDK^BNJbPaAgfLXb=0FKbQ?6bcd}E~-Te)uyAC(E$BJi+GEQZYz5XwOEf!~S4C^_lEyfp0!b=)_? zkh=p|bmPbC>UHm0?ATG!wu<=00A77-I;5xV9&t{@LYaf z?XCiRm185)Y1|cxLH1xD4{TZ}z_s25v`4kNt2cKbcSmZ0Z&W(%FU&RVk7l>jhof=? zn1JWD#+M0$0JF`dnxIQiDcAm0T^;ED$TW^n$8isSw(3sGBdy^Ezt5i<;1`J1Eit8J zS*~dyKLyOK*aT?6UYX9l?4SIx!uQhjd}}3k8?DCdce(|REoWim*E`C-%BACh`xyGm zVDj646~0MWcrI1WTU$1rr}0R6AjzMQW7f6+^AqvselqdB81h|{d<`^;B4(OPtnVMH z0D4VAbnGw^$MI>u(-NflQbbvD@|G#LECBg1vj&cVOzTL;k3PI(@Y@Tz^!TD(ZW<72 zWd)j1lX=?$JgxsZ#rL_7Z_32^m3apXI|RLOtLiY)-~17I);D?NKIIp^I{EsSBh2-8 zl~bE*Os#c|9BY-%$kmRYU$QWPr4#k^zF7Ht=~=(7U0~{oIZ3l0_=nrABry* zt_L``JWq=#d9tJm01hx}6QYJDIW>oNo+LsR_>f5J3XfkF$K&l&;1`MSutk@5k?K?0 zN6Y++MPbr*YGFnmq1EdkK+zYU{vwTpQN;A(oB}iLNC_cUv(>qsEKR>L4l}J5bjH}` zKKCg&HDwsR56AH<6IG2BIO>rnw{JQ?)J>`_tSsyK|66w{H*gsN{*O#dF3h?+lGC^@ z5h6FY`ZnE8OeMMR4(o6C>GtV5LhZv9=7`qG=1`&}%O-*b-$4Yc9h_abV1XGaJX7(0 zB4!?T4Z#e_Q^J#rGl@kk0$z8#8-%E_u+k*V(zN?zO#@bhwjVvMFQx%~!v~RHMRJZE zJ+D)^qR&kS*@GvdU3vv$43l%!2;j8<$={Uzb(&sjHJMpn5QFOipeT>(pm*vpa(%he zb?q*%8k`ousCKfA*mu?Q$t{w}=OM-?Db6XAUd+lk?fo`eEiTv#z~}FcKlWXn0Et+v zIXi#BK6qU#;Jl2iLx?JyzPxjgK!TjD15BPAxpuNnCz~7_3BDW`2NCq*H3i;vbz@+1 z{ydSeOa}hk2XG+)dgFy)<~TS#J8hppCE(==I_i7V6^Y^X6uLCoVF5Dm#k#^}0eqIJ zlJ?^OUjnLuGE9AxSke0}bU3>lCni&nA#vTce{YlU<3vl7N5WAI2v1W4qIGBI&s z(gxn8{Z*}iwGSWICF$Je@e(@y_i8J=mrxgc0i?DBh;%jv5>zoZ$TlLbNswy)`AH(% z8S&pt!7C-D|K=RQpPBeU(G37+=g!dzTQqul?G9UjmjLgIJNMz*rw0T{jNGEjK~9&X z<++zW%0@{KTET5wr*}_z_N;!Gc6;kCqv%_jZA`47LPGeXqOO)&fbsYbi2i4qZiOLd zXOjnqE%xaFc7SNrEz%wzm>%GLrrdjn(B!qh=pR1nXg@g8YK(G36mdx$OY1%DL^!iB z$NaMOukw%hR{yC~nF0vZG;sXfX{BA*C+J$N_y)=~T~sx*RkqyCA(1YuE9>a12G_C3 z_g$VhqTHn`dV!oqC3BQCS0C)7s0#x)9QJ#S&>Pa-6BoJ+K66Y;9S2%5(|G5zU z*pF%~l?Y(s+2}WA-b<6G96+a`hZxq03E*Aey5@23rW{7jhda`^0$4kaTvj1xy=1@0 z=edZGVdWu#$s+d1quaRJ!ilK~LUHXM!5@x`mwbFui2wkj@cW3H31?@fSX^H6kU*S} zPwTCLOWW$fb&;W9E7DYbb4bqnh5t2F1is9ZzzAeqgs))E?Q||-NfhAN_iIC+w=WlbQqo2;ox*&(iU;Cs>jX9u$I40r*AjrS_+xB94{s+mvhAWdU;9 zFZw0V!FfJXTtKcCc)Ly%MI4zprfdAi3OmZAHUaL9|3N(SE`?#p(No8iX;Ps!U$wwb zLyyIfz$4<$=|F(&ysmeF@2~>S^K+e#J}=ksRMdEfoR~bxqHz2ec;X)TvpV~zO#pxh z{vD8gg8JmylPoMP`Uk*u=C#1bGsp$N`=*K9^8j+1kfaj(F9dS6T3x-GlrRiArnDdU zn5pTw?uY>6@%w|HPt#Ep@#691eS=a!U3)C4+ll4yP79tZ0Oxu`_0QF3r*y8>Rpr>k zF_u=W{{z~y6TVh=KOGSuY4|DPOG5z8Or2)xTvJE#&Tcr~6A?4Zq|qtC_T+?GGtDgFbXj&w`_fI0YOgu`KBae<>J4!bM+tbkRx zhHZRg@LC@&QDL%Ho5HrLl1iSQnqc~Z^%0i~E%=2hy>ZE^2=L>u6v5BSykjR0GrutB z(BukovD!$pE8hilA)||;T*ex;$rm(RO}>BZdEN5kZ-?Xg9afW75ddI3escILX*vu; zo;|eBdMGiL0)Xn8-I5}|M6~~OF@!LTL;Yur89}r*y0tQ^TSlf;wmEe8IhO5(-xH_d zzZ9KunXH-s08RJ^;?C2|?0F6!%`r6*iY^TjgJ)?p3VgApB*@Z^&DyuN6v+X-pldP~h4Qqbq!Ko}=BLF}X9spiU(@V>X96Y#JXbTjZzBDW|=c-;#TV3WnYJ(qz?JY}t z!1t2D1>$PK&$oZ^=pio5oR`Bq(}JJ819G4L>O{IqfFFVLz&}BBZgO&h7Y`Rcmng)g z@sT5`2F;2qE0ArWN8`ghDO`k*`_~qxlmwlDKXK*+$4(s41M2lS|X zX$&4L7|7bvbpim~1D^)|yUcs;*{8X1eyaK)kd$YEB!NTgtAJm$2BLF1sYeE16(FnZXz!Pa+7>4ZM`yCb+=2dM8W*Q3MbCy62 zo@)xo&G{l8O6^ycAuJ$)?`k1u1vC+{^GZn2Y_70x|C4(0e7pzVafi6~(k)pN0zmu> zp@DY*FQs|Q%S$}<#NQ+&5WqTH9{A2$-~($+0Dybo zNdrHHxH4FnpJne8-(+QJi5v;6xQhs7T3-OZb6N-;1StX3vIwd#vhPl?ZLJ3gTP^nQ zdot%ZGw_jdwU+9(tT_PycEbM#{wcz3IX8PDCc*MD7O=h*y_^EPz3nm(h4?PELPvug?r&{*pOr1FPw;C-=^=$$0a;N_)7rp}&X z^J}h&-!_&{;23?+psN2^SPg4!QBd6?TUx)w(sVivh%MIq$C#Qrt$X#!G5BP~6JC{c z)hk~`9)h1Hew-|`J_Ccp{NP)5GB7-X`>PyP-^(ZeBoI(~t2hH)hXtlS)qa@kAmKagm@vd)?DPH3Fk@dEt%PWV^GZ?C#8FAD(x_P~3h_T5 zb#Oj92EW#&VXY-ED**r=g1=Av1(LxuF9;gk{HC|E`Ks$`g0JR$=cDKj26!z1Y8s#d z_-+DRI5)+!j|>$&a#2`?5yw+hqaVTf&c&y!!03v6}V33 zUv>T8;dQsZ8Pm}JT_^!}fS)6Q6(K`Bw-CfZf)3UK3HZm4exK(L?Q4@Mmbnn_zZd>$ z6+_k)xnu|c@D143h)Hmh%-^`>N^W`cUG((z=P&boL`bF5)502j+`vO+@WW<{L*ILf zQzwt=gH1;8560oqio>#|a!C;YU^i?w@e7W3%lx5{QFi>$4>L5fUV$$!0Uco~xv4;a zuG1Q98JAOl^BzRR^3nnap8gK!&*#3*elP*w_r&Z)mrITS0Q;fuOuR7qX_+518r*o( zZCrEX8>)d{Y5vDH&z1$i2EBrFXnOKA&+dPM<)uY+5WXJ4yT&Vgx94KYB}sr(_P~cC z_)jR8^_Huy<@LASNl#y&O$6&*K5YS1+Mbi!0L!u*@OU&IqA=vh3(s=o#e?~M!{BKcBjDg#5q-1Nq`uzu54+X|F<|6EdP0l)^GcASX0ne!an z|0FXPa>~7E;6pp%b5+mxV#_5@0DwJkT@*iZ{=>TLimg}ky4&7N|Gh&1ORv(hGyc=q5LAyugeDphq?Ot8@ck@*V5acD{hOP4sseFhplFliIYb;e)I*` zO&o|nn)Uf6eC$5(ybpJ2$}5rpsqBH*Me!$W|DLPd1Oc0_cnw>wx|U6wucWt6k|AwheSHJ0G?!RfSYUo`hS{0(%$%QQ=E5}jSKKXm-oU5sfd@^&A2M9(@~R*J zKm_Jtct;e!sy?<>$1(6k1fPxIE92s;sjpOd)eu0+ZukM5BPcrUt0&eE=4b1H{?}K04ZWw|Vu<+8k5<^KV`V=25w0000< KMNUMnLSTaFk*V7N literal 0 HcmV?d00001 diff --git a/src/karts/kart_gfx.cpp b/src/karts/kart_gfx.cpp index 989495245..38fdcf1b6 100644 --- a/src/karts/kart_gfx.cpp +++ b/src/karts/kart_gfx.cpp @@ -142,7 +142,8 @@ void KartGFX::addEffect(KartGFXType type, const std::string &file_name, const Vec3 &position, bool important) { if (!UserConfigParams::m_graphical_effects && - (!important || m_kart->getType() == RaceManager::KT_AI)) + (!important || m_kart->getType() == RaceManager::KT_AI || + m_kart->getType() == RaceManager::KT_SPARE_TIRE)) { m_all_emitters.push_back(NULL); return; diff --git a/src/karts/kart_properties_manager.cpp b/src/karts/kart_properties_manager.cpp index a9f58429c..3398794d8 100644 --- a/src/karts/kart_properties_manager.cpp +++ b/src/karts/kart_properties_manager.cpp @@ -494,7 +494,7 @@ const std::vector KartPropertiesManager::getKartsInGroup( * to this list. */ void KartPropertiesManager::getRandomKartList(int count, - RemoteKartInfoList& existing_karts, + RemoteKartInfoList* existing_karts, std::vector *ai_list) { // First: set up flags (based on global kart @@ -504,18 +504,22 @@ void KartPropertiesManager::getRandomKartList(int count, used.resize(getNumberOfKarts(), false); std::vector random_kart_queue; - for (unsigned int i=0; isize(); i++) { - int id = getKartId(existing_karts[i].getKartName()); - used[id] = true; - } - catch (std::runtime_error& ex) - { - (void)ex; - Log::error("[KartPropertiesManager]", "getRandomKartList : WARNING, " - "can't find kart '%s'", existing_karts[i].getKartName().c_str()); + try + { + int id = getKartId((*existing_karts)[i].getKartName()); + used[id] = true; + } + catch (std::runtime_error& ex) + { + (void)ex; + Log::error("[KartPropertiesManager]", "getRandomKartList : " + "WARNING, can't find kart '%s'", + (*existing_karts)[i].getKartName().c_str()); + } } } for(unsigned int i=0; isize(); i++) diff --git a/src/karts/kart_properties_manager.hpp b/src/karts/kart_properties_manager.hpp index af621b4e7..240413a80 100644 --- a/src/karts/kart_properties_manager.hpp +++ b/src/karts/kart_properties_manager.hpp @@ -94,7 +94,7 @@ public: void selectKartName(const std::string &kart_name); bool testAndSetKart(int kartid); void getRandomKartList(int count, - RemoteKartInfoList& existing_karts, + RemoteKartInfoList* existing_karts, std::vector *ai_list); void setHatMeshName(const std::string &hat_name); // ------------------------------------------------------------------------ diff --git a/src/modes/soccer_world.cpp b/src/modes/soccer_world.cpp index b7f565805..bb6422486 100644 --- a/src/modes/soccer_world.cpp +++ b/src/modes/soccer_world.cpp @@ -30,6 +30,7 @@ #include "karts/kart_properties.hpp" #include "karts/rescue_animation.hpp" #include "karts/controller/local_player_controller.hpp" +#include "karts/controller/network_player_controller.hpp" #include "physics/physics.hpp" #include "states_screens/race_gui_base.hpp" #include "tracks/track.hpp" @@ -421,11 +422,9 @@ AbstractKart *SoccerWorld::createKart(const std::string &kart_ident, int index, m_num_players ++; break; case RaceManager::KT_NETWORK_PLAYER: - break; // Avoid compiler warning about enum not handled. - //controller = new NetworkController(kart_ident, position, init_pos, - // global_player_id); - //m_num_players++; - //break; + controller = new NetworkPlayerController(new_kart); + m_num_players++; + break; case RaceManager::KT_AI: controller = loadAIController(new_kart); break; @@ -433,6 +432,8 @@ AbstractKart *SoccerWorld::createKart(const std::string &kart_ident, int index, break; case RaceManager::KT_LEADER: break; + case RaceManager::KT_SPARE_TIRE: + break; } new_kart->setController(controller); diff --git a/src/modes/three_strikes_battle.cpp b/src/modes/three_strikes_battle.cpp index 96967e881..df82b8c2a 100644 --- a/src/modes/three_strikes_battle.cpp +++ b/src/modes/three_strikes_battle.cpp @@ -28,6 +28,7 @@ #include "karts/controller/spare_tire_ai.hpp" #include "karts/kart_model.hpp" #include "karts/kart_properties.hpp" +#include "karts/kart_properties_manager.hpp" #include "physics/physics.hpp" #include "states_screens/race_gui_base.hpp" #include "tracks/arena_graph.hpp" @@ -102,12 +103,10 @@ void ThreeStrikesBattle::reset() for(unsigned int n=0; n(m_karts[n]->getController()) != NULL) { // STA has no life m_kart_info[n].m_lives = 0; - is_sta = true; } else { @@ -127,11 +126,11 @@ void ThreeStrikesBattle::reset() if (core::stringc(curr->getName()) == "tire1") { - curr->setVisible(!is_sta); + curr->setVisible(true); } else if (core::stringc(curr->getName()) == "tire2") { - curr->setVisible(!is_sta); + curr->setVisible(true); } } @@ -174,6 +173,20 @@ void ThreeStrikesBattle::reset() */ void ThreeStrikesBattle::kartAdded(AbstractKart* kart, scene::ISceneNode* node) { + if (kart->getType() == RaceManager::KartType::KT_SPARE_TIRE) + { + // Add heart billboard above it + video::ITexture *heart = + irr_driver->getTexture(FileManager::GUI, "heart.png"); + float height = kart->getKartHeight() + 0.5f; + + scene::ISceneNode* billboard = irr_driver->addBillboard + (core::dimension2d(0.8f, 0.8f), heart, kart->getNode(), + true); + billboard->setPosition(core::vector3df(0, height, 0)); + return; + } + float coord = -kart->getKartLength()*0.5f; scene::IMeshSceneNode* tire_node = irr_driver->addMesh(m_tire, "3strikestire", node); @@ -346,18 +359,6 @@ void ThreeStrikesBattle::update(float dt) WorldWithRank::update(dt); WorldWithRank::updateTrack(dt); - if (getPhase() == World::GO_PHASE) - { - // Eliminate all spare tire karts first, they will be spawned if needed - for (unsigned int i = 0; i < m_spare_tire_karts.size(); i++) - { - if (!m_spare_tire_karts[i]->isEliminated()) - { - m_spare_tire_karts[i]->eliminate(); - } - } - } - spawnSpareTireKarts(); if (m_track->hasNavMesh()) updateSectorForKarts(); @@ -682,7 +683,8 @@ void ThreeStrikesBattle::loadCustomModels() if (all_nodes > 200) { const unsigned int max_sta_num = unsigned(m_karts.size() * 0.8f); - unsigned int sta_created = 0; + unsigned int pos_created = 0; + std::vector pos; for (int i = 0; i < all_nodes; i++) { // Pre-spawn the spare tire karts on the item position, @@ -692,22 +694,33 @@ void ThreeStrikesBattle::loadCustomModels() btTransform t; t.setOrigin(item->getXYZ()); t.setRotation(item->getRotation()); + pos.push_back(t); + pos_created++; + if (pos_created >= max_sta_num) break; + } - AbstractKart* sta = new Kart("nolok", m_karts.size(), - m_karts.size() + 1, t, PLAYER_DIFFICULTY_NORMAL, KRT_RED); - sta->init(RaceManager::KartType::KT_AI); + // Compute a random kart list + std::vector sta_list; + kart_properties_manager->getRandomKartList(pos.size(), NULL, + &sta_list); + + assert(sta_list.size() == pos.size()); + // Now add them + for (unsigned int i = 0; i < pos.size(); i++) + { + AbstractKart* sta = new Kart(sta_list[i], m_karts.size(), + m_karts.size() + 1, pos[i], PLAYER_DIFFICULTY_NORMAL, + KRT_RED); + sta->init(RaceManager::KartType::KT_SPARE_TIRE); sta->setController(new SpareTireAI(sta)); m_karts.push_back(sta); - race_manager->addSpareTireKartStatus(); + race_manager->addSpareTireKartStatus(sta_list[i]); m_track->adjustForFog(sta->getNode()); // Copy STA pointer to m_spare_tire_karts array, allowing them // to respawn easily m_spare_tire_karts.push_back(sta); - - sta_created++; - if (sta_created >= max_sta_num) break; } race_manager->setNumKarts(m_karts.size()); } diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 3b14eae12..b36e3d7be 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -372,6 +372,8 @@ AbstractKart *World::createKart(const std::string &kart_ident, int index, break; case RaceManager::KT_LEADER: break; + case RaceManager::KT_SPARE_TIRE: + break; } new_kart->setController(controller); diff --git a/src/race/race_manager.cpp b/src/race/race_manager.cpp index 84fe2dc4f..d046482c6 100644 --- a/src/race/race_manager.cpp +++ b/src/race/race_manager.cpp @@ -288,7 +288,7 @@ void RaceManager::computeRandomKartList() } if(n>0) - kart_properties_manager->getRandomKartList(n, m_player_karts, + kart_properties_manager->getRandomKartList(n, &m_player_karts, &m_ai_kart_list ); if (m_ai_kart_override != "") diff --git a/src/race/race_manager.hpp b/src/race/race_manager.hpp index c670f5e19..b9aeb3151 100644 --- a/src/race/race_manager.hpp +++ b/src/race/race_manager.hpp @@ -236,9 +236,10 @@ public: DIFFICULTY_COUNT}; /** Different kart types: A local player, a player connected via network, - * an AI kart, the leader kart (currently not used), a ghost kart. */ + * an AI kart, the leader kart (currently not used), a ghost kart and + * spare tire karts which allow gain life in battle mode */ enum KartType { KT_PLAYER, KT_NETWORK_PLAYER, KT_AI, KT_LEADER, - KT_GHOST }; + KT_GHOST, KT_SPARE_TIRE }; private: bool m_started_from_overworld; @@ -752,10 +753,10 @@ public: return m_watching_replay; } // isWatchingReplay // ------------------------------------------------------------------------ - void addSpareTireKartStatus() + void addSpareTireKartStatus(const std::string& name) { - m_kart_status.push_back(KartStatus("nolok", 0, -1, -1, - -1, KT_AI, PLAYER_DIFFICULTY_NORMAL)); + m_kart_status.push_back(KartStatus(name, 0, -1, -1, + -1, KT_SPARE_TIRE, PLAYER_DIFFICULTY_NORMAL)); } // addSpareTireKartStatus }; // RaceManager diff --git a/src/states_screens/race_gui.cpp b/src/states_screens/race_gui.cpp index 051ff6659..d09270531 100644 --- a/src/states_screens/race_gui.cpp +++ b/src/states_screens/race_gui.cpp @@ -385,7 +385,9 @@ void RaceGUI::drawGlobalMiniMap() world->getTrack()->mapPoint2MiniMap(xyz, &draw_at); draw_at *= UserConfigParams::m_scale_rtts_factor; - video::ITexture* icon = kart->getKartProperties()->getMinimapIcon(); + video::ITexture* icon = sta ? + irr_driver->getTexture(FileManager::GUI, "heart.png") : + kart->getKartProperties()->getMinimapIcon(); // int marker_height = m_marker->getSize().Height; core::rect source(core::position2di(0, 0), icon->getSize()); From ee17928382876226a0082d60458c6eb40e643d80 Mon Sep 17 00:00:00 2001 From: Legimet Date: Sat, 8 Oct 2016 12:07:39 -0400 Subject: [PATCH 283/350] Fix credits for Boom-boom-boom song --- data/CREDITS | Bin 16934 -> 16952 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/data/CREDITS b/data/CREDITS index 1307b2ce0c9879c0ff8dd6d171e36b1477c2dc31..0837931a4d00a4d9b8373bdbc26b8566632e71af 100755 GIT binary patch delta 54 zcmZ41!nmV_aYK%rx;H~ALncEBLk5EagA+p{LnT8FkY5bsB{F0Ku>wOFke|D`QEsU+ E0C!Ui_5c6? delta 36 pcmdnd!nmx3aYK%rs4qhzLkSQnFoZB Date: Sat, 8 Oct 2016 21:25:56 +0200 Subject: [PATCH 284/350] Fixed displaying some special characters in keys binding in options. It most probably needs to be done this way since commit 2096532d0e654a2f67c968879a86e1b4b8298007. I don't know what about OSX, so I enabled it only for linux. --- src/input/binding.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input/binding.cpp b/src/input/binding.cpp index 4784d0a04..1c307e870 100644 --- a/src/input/binding.cpp +++ b/src/input/binding.cpp @@ -96,7 +96,7 @@ irr::core::stringw Binding::getAsString() const switch(m_id) { -#ifdef WIN32 +#if defined(WIN32) || (defined(__linux__) && !defined(ANDROID)) // Windows codes certain special keys, which have atm no defined // value in irr::KEY_*. So for now hardcode the values. // FIXME: what happens with international keyboards? E.g. A [ From 357567ae8d87805cde19786ffecc0eaf624078c9 Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 9 Oct 2016 09:50:26 +0800 Subject: [PATCH 285/350] Move spare tire num info to race_manager It allows this info to be get more easily --- src/modes/three_strikes_battle.cpp | 2 ++ src/modes/three_strikes_battle.hpp | 3 --- src/modes/world.cpp | 1 + src/modes/world_status.cpp | 12 ++++++++---- src/race/race_manager.cpp | 1 + src/race/race_manager.hpp | 12 ++++++++++++ src/states_screens/race_gui_base.cpp | 8 ++------ src/states_screens/race_result_gui.cpp | 7 ++----- 8 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/modes/three_strikes_battle.cpp b/src/modes/three_strikes_battle.cpp index df82b8c2a..af07cd973 100644 --- a/src/modes/three_strikes_battle.cpp +++ b/src/modes/three_strikes_battle.cpp @@ -723,6 +723,8 @@ void ThreeStrikesBattle::loadCustomModels() m_spare_tire_karts.push_back(sta); } race_manager->setNumKarts(m_karts.size()); + assert(m_spare_tire_karts.size() == + race_manager->getNumSpareTireKarts()); } } } // loadCustomModels diff --git a/src/modes/three_strikes_battle.hpp b/src/modes/three_strikes_battle.hpp index 5f0a700af..184b9dc73 100644 --- a/src/modes/three_strikes_battle.hpp +++ b/src/modes/three_strikes_battle.hpp @@ -133,9 +133,6 @@ public: bool spareTireKartsSpawned() const; // ------------------------------------------------------------------------ void spawnSpareTireKarts(); - // ------------------------------------------------------------------------ - unsigned int getNumSpareTireKarts() const - { return m_spare_tire_karts.size(); } }; // ThreeStrikesBattles diff --git a/src/modes/world.cpp b/src/modes/world.cpp index b36e3d7be..8cb7e918c 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -483,6 +483,7 @@ World::~World() race_manager->setRecordRace(false); race_manager->setWatchingReplay(false); race_manager->setTimeTarget(0.0f); + race_manager->setSpareTireKartNum(0); Camera::removeAllCameras(); diff --git a/src/modes/world_status.cpp b/src/modes/world_status.cpp index c863caa0c..37fa95187 100644 --- a/src/modes/world_status.cpp +++ b/src/modes/world_status.cpp @@ -195,7 +195,8 @@ void WorldStatus::updateTime(const float dt) m_auxiliary_timer += dt; if (UserConfigParams::m_artist_debug_mode && - race_manager->getNumberOfKarts() == 1 && + race_manager->getNumberOfKarts() - + race_manager->getNumSpareTireKarts() == 1 && race_manager->getTrackName() != "tutorial") { m_auxiliary_timer += dt * 6; @@ -247,7 +248,8 @@ void WorldStatus::updateTime(const float dt) // In artist debug mode, when without opponents, skip the ready/set/go counter faster if (UserConfigParams::m_artist_debug_mode && - race_manager->getNumberOfKarts() == 1 && + race_manager->getNumberOfKarts() - + race_manager->getNumSpareTireKarts() == 1 && race_manager->getTrackName() != "tutorial") { m_auxiliary_timer += dt*6; @@ -272,7 +274,8 @@ void WorldStatus::updateTime(const float dt) // In artist debug mode, when without opponents, skip the ready/set/go counter faster if (UserConfigParams::m_artist_debug_mode && - race_manager->getNumberOfKarts() == 1 && + race_manager->getNumberOfKarts() - + race_manager->getNumSpareTireKarts() == 1 && race_manager->getTrackName() != "tutorial") { m_auxiliary_timer += dt*6; @@ -296,7 +299,8 @@ void WorldStatus::updateTime(const float dt) // In artist debug mode, when without opponents, skip the ready/set/go counter faster if (UserConfigParams::m_artist_debug_mode && - race_manager->getNumberOfKarts() == 1 && + race_manager->getNumberOfKarts() - + race_manager->getNumSpareTireKarts() == 1 && race_manager->getTrackName() != "tutorial") { m_auxiliary_timer += dt*6; diff --git a/src/race/race_manager.cpp b/src/race/race_manager.cpp index d046482c6..011208291 100644 --- a/src/race/race_manager.cpp +++ b/src/race/race_manager.cpp @@ -84,6 +84,7 @@ RaceManager::RaceManager() setTrack("jungle"); m_default_ai_list.clear(); setNumPlayers(0); + setSpareTireKartNum(0); } // RaceManager //----------------------------------------------------------------------------- diff --git a/src/race/race_manager.hpp b/src/race/race_manager.hpp index b9aeb3151..46326887f 100644 --- a/src/race/race_manager.hpp +++ b/src/race/race_manager.hpp @@ -330,6 +330,7 @@ private: GrandPrixData m_grand_prix; SavedGrandPrix* m_saved_gp; int m_num_karts; + unsigned int m_num_spare_tire_karts; unsigned int m_num_finished_karts; unsigned int m_num_finished_players; int m_coin_target; @@ -757,7 +758,18 @@ public: { m_kart_status.push_back(KartStatus(name, 0, -1, -1, -1, KT_SPARE_TIRE, PLAYER_DIFFICULTY_NORMAL)); + m_num_spare_tire_karts++; } // addSpareTireKartStatus + // ------------------------------------------------------------------------ + void setSpareTireKartNum(unsigned int i) + { + m_num_spare_tire_karts = i; + } // setSpareTireKartNum + // ------------------------------------------------------------------------ + unsigned int getNumSpareTireKarts() const + { + return m_num_spare_tire_karts; + } // getNumSpareTireKarts }; // RaceManager diff --git a/src/states_screens/race_gui_base.cpp b/src/states_screens/race_gui_base.cpp index 2428c33b2..930941f0d 100644 --- a/src/states_screens/race_gui_base.cpp +++ b/src/states_screens/race_gui_base.cpp @@ -42,7 +42,7 @@ #include "karts/kart_properties_manager.hpp" #include "karts/rescue_animation.hpp" #include "modes/linear_world.hpp" -#include "modes/three_strikes_battle.hpp" +#include "modes/world.hpp" #include "tracks/track.hpp" #include "utils/constants.hpp" @@ -638,11 +638,7 @@ void RaceGUIBase::drawGlobalPlayerIcons(int bottom_margin) y_space = irr_driver->getActualScreenSize().Height - y_base; } - unsigned int sta = 0; - ThreeStrikesBattle* tsb = dynamic_cast(World::getWorld()); - if (tsb) - sta = tsb->getNumSpareTireKarts(); - + unsigned int sta = race_manager->getNumSpareTireKarts(); const unsigned int num_karts = race_manager->getNumberOfKarts() - sta; // -2 because that's the spacing further on diff --git a/src/states_screens/race_result_gui.cpp b/src/states_screens/race_result_gui.cpp index 972c8dab3..109c695f1 100644 --- a/src/states_screens/race_result_gui.cpp +++ b/src/states_screens/race_result_gui.cpp @@ -43,7 +43,7 @@ #include "modes/demo_world.hpp" #include "modes/overworld.hpp" #include "modes/soccer_world.hpp" -#include "modes/three_strikes_battle.hpp" +#include "modes/world_with_rank.hpp" #include "network/protocol_manager.hpp" #include "network/protocols/client_lobby_room_protocol.hpp" #include "race/highscores.hpp" @@ -460,12 +460,9 @@ void RaceResultGUI::backToLobby() WorldWithRank *rank_world = (WorldWithRank*)World::getWorld(); unsigned int first_position = 1; - unsigned int sta = 0; + unsigned int sta = race_manager->getNumSpareTireKarts(); if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER) first_position = 2; - ThreeStrikesBattle* tsb = dynamic_cast(World::getWorld()); - if (tsb) - sta = tsb->getNumSpareTireKarts(); // Use only the karts that are supposed to be displayed (and // ignore e.g. the leader in a FTL race). From 6b8156c254ab77511179172b46b4fa6ff74c9cfd Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 9 Oct 2016 11:17:36 +0800 Subject: [PATCH 286/350] Pre-spawn spare tire karts on random nodes in graph Don't use the item location, as it can be remembered by players --- src/items/item.cpp | 5 +-- src/items/item.hpp | 3 -- src/modes/three_strikes_battle.cpp | 51 ++++++++++++++++++++---------- src/race/race_manager.hpp | 5 +-- src/tracks/track.cpp | 6 +--- src/tracks/track.hpp | 8 +++++ 6 files changed, 48 insertions(+), 30 deletions(-) diff --git a/src/items/item.cpp b/src/items/item.cpp index 389154e2a..8f3eb7d07 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -43,10 +43,7 @@ Item::Item(ItemType type, const Vec3& xyz, const Vec3& normal, m_distance_2 = 1.2f; initItem(type, xyz); - Vec3 axis = -normal.cross(Vec3(0, 1, 0)); - if (axis.length() == 0) - axis = Vec3(0, 0, 1); - m_original_rotation = btQuaternion(axis, normal.angle(Vec3(0, 1, 0))); + m_original_rotation = Track::createRotationFromNormal(normal); m_rotation_angle = 0.0f; m_original_mesh = mesh; m_original_lowmesh = lowres_mesh; diff --git a/src/items/item.hpp b/src/items/item.hpp index 99b1b3abf..763b3008c 100644 --- a/src/items/item.hpp +++ b/src/items/item.hpp @@ -276,9 +276,6 @@ public: { return (scene::ISceneNode *) m_node; } - // ------------------------------------------------------------------------ - const btQuaternion& getRotation() const { return m_original_rotation; } - }; // class Item #endif diff --git a/src/modes/three_strikes_battle.cpp b/src/modes/three_strikes_battle.cpp index af07cd973..f5c06d996 100644 --- a/src/modes/three_strikes_battle.cpp +++ b/src/modes/three_strikes_battle.cpp @@ -23,7 +23,6 @@ #include "graphics/camera.hpp" #include "graphics/irr_driver.hpp" #include "io/file_manager.hpp" -#include "items/item_manager.hpp" #include "karts/kart.hpp" #include "karts/controller/spare_tire_ai.hpp" #include "karts/kart_model.hpp" @@ -32,10 +31,12 @@ #include "physics/physics.hpp" #include "states_screens/race_gui_base.hpp" #include "tracks/arena_graph.hpp" +#include "tracks/arena_node.hpp" #include "tracks/track.hpp" #include "tracks/track_object_manager.hpp" #include "utils/constants.hpp" +#include #include #include @@ -675,27 +676,44 @@ void ThreeStrikesBattle::spawnSpareTireKarts() //----------------------------------------------------------------------------- void ThreeStrikesBattle::loadCustomModels() { - // Pre-add spare tire karts - if (ArenaGraph::get()) + // Pre-add spare tire karts if there are more than certain number of karts + ArenaGraph* ag = ArenaGraph::get(); + if (ag && m_karts.size() > 4) { // Spare tire karts only added with large arena - const int all_nodes = ArenaGraph::get()->getNumNodes(); - if (all_nodes > 200) + const int all_nodes = ag->getNumNodes(); + if (all_nodes > 500) { + // Don't create too many spare tire karts const unsigned int max_sta_num = unsigned(m_karts.size() * 0.8f); unsigned int pos_created = 0; + std::vector used; std::vector pos; - for (int i = 0; i < all_nodes; i++) + + // Fill all current starting position into used first + for (unsigned int i = 0; i < getNumberOfRescuePositions(); i++) { - // Pre-spawn the spare tire karts on the item position, - // preven affecting current karts - Item* item = ItemManager::get()->getFirstItemInQuad(i); - if (item == NULL) continue; + int node = -1; + ag->findRoadSector(getRescueTransform(i).getOrigin(), &node, + NULL, true); + assert(node != -1); + used.push_back(node); + } + + // Find random nodes to pre-spawn spare tire karts + RandomGenerator random; + while (true) + { + const int node = random.get(all_nodes); + if (std::find(used.begin(), used.end(), node) != used.end()) + continue; + const ArenaNode* n = ag->getNode(node); btTransform t; - t.setOrigin(item->getXYZ()); - t.setRotation(item->getRotation()); + t.setOrigin(n->getCenter()); + t.setRotation(Track::createRotationFromNormal(n->getNormal())); pos.push_back(t); pos_created++; + used.push_back(node); if (pos_created >= max_sta_num) break; } @@ -715,16 +733,17 @@ void ThreeStrikesBattle::loadCustomModels() sta->setController(new SpareTireAI(sta)); m_karts.push_back(sta); - race_manager->addSpareTireKartStatus(sta_list[i]); + race_manager->addSpareTireKart(sta_list[i]); m_track->adjustForFog(sta->getNode()); // Copy STA pointer to m_spare_tire_karts array, allowing them // to respawn easily m_spare_tire_karts.push_back(sta); } - race_manager->setNumKarts(m_karts.size()); - assert(m_spare_tire_karts.size() == - race_manager->getNumSpareTireKarts()); + unsigned int sta_num = race_manager->getNumSpareTireKarts(); + assert(m_spare_tire_karts.size() == sta_num); + Log::info("ThreeStrikesBattle","%d spare tire kart(s) created.", + sta_num); } } } // loadCustomModels diff --git a/src/race/race_manager.hpp b/src/race/race_manager.hpp index 46326887f..7be0e8227 100644 --- a/src/race/race_manager.hpp +++ b/src/race/race_manager.hpp @@ -754,12 +754,13 @@ public: return m_watching_replay; } // isWatchingReplay // ------------------------------------------------------------------------ - void addSpareTireKartStatus(const std::string& name) + void addSpareTireKart(const std::string& name) { m_kart_status.push_back(KartStatus(name, 0, -1, -1, -1, KT_SPARE_TIRE, PLAYER_DIFFICULTY_NORMAL)); m_num_spare_tire_karts++; - } // addSpareTireKartStatus + m_num_karts++; + } // addSpareTireKart // ------------------------------------------------------------------------ void setSpareTireKartNum(unsigned int i) { diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index a24d79a6b..dce9fe474 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -702,11 +702,7 @@ btQuaternion Track::getArenaStartRotation(const Vec3& xyz, float heading) } const Vec3& normal = Graph::get()->getQuad(node)->getNormal(); - Vec3 axis = -normal.cross(Vec3(0, 1, 0)); - if (axis.length() == 0) - axis = Vec3(0, 0, 1); - - btQuaternion q(axis, normal.angle(Vec3(0, 1, 0))); + btQuaternion q = createRotationFromNormal(normal); btMatrix3x3 m; m.setRotation(q); return btQuaternion(m.getColumn(1), heading * DEGREE_TO_RAD) * q; diff --git a/src/tracks/track.hpp b/src/tracks/track.hpp index 03e4972f4..8f9bcd05a 100644 --- a/src/tracks/track.hpp +++ b/src/tracks/track.hpp @@ -397,6 +397,14 @@ public: static const float NOHIT; + static btQuaternion createRotationFromNormal(const Vec3& normal) + { + Vec3 axis = -normal.cross(Vec3(0, 1, 0)); + if (axis.length() == 0) + axis = Vec3(0, 0, 1); + return btQuaternion(axis, normal.angle(Vec3(0, 1, 0))); + } // createRotationFromNormal + Track (const std::string &filename); ~Track (); void cleanup (); From fb0f4fca3c039c6a6becec3f2766595be82b3308 Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 9 Oct 2016 13:24:00 +0800 Subject: [PATCH 287/350] Allow BattleAI to collect lives --- src/karts/controller/arena_ai.cpp | 3 +- src/karts/controller/arena_ai.hpp | 4 +- src/karts/controller/battle_ai.cpp | 52 ++++++++++++++++++++------ src/karts/controller/battle_ai.hpp | 2 +- src/karts/controller/soccer_ai.cpp | 3 +- src/karts/controller/soccer_ai.hpp | 2 +- src/karts/controller/spare_tire_ai.hpp | 3 +- src/modes/three_strikes_battle.cpp | 4 +- src/modes/world.cpp | 2 +- src/modes/world_with_rank.cpp | 2 +- src/states_screens/race_gui.cpp | 2 +- 11 files changed, 53 insertions(+), 26 deletions(-) diff --git a/src/karts/controller/arena_ai.cpp b/src/karts/controller/arena_ai.cpp index 0785829ff..5a6f2ec28 100644 --- a/src/karts/controller/arena_ai.cpp +++ b/src/karts/controller/arena_ai.cpp @@ -116,7 +116,6 @@ void ArenaAI::update(float dt) if (gettingUnstuck(dt)) return; - findClosestKart(true); findTarget(); // After found target, convert it to local coordinate, used for skidding or @@ -414,7 +413,7 @@ void ArenaAI::useItems(const float dt) return; // Find a closest kart again, this time we ignore difficulty - findClosestKart(false); + findClosestKart(false/*use_difficulty*/, false/*find_sta*/); if (!m_closest_kart) return; Vec3 closest_kart_point_lc = diff --git a/src/karts/controller/arena_ai.hpp b/src/karts/controller/arena_ai.hpp index 40adaaac5..7521e2746 100644 --- a/src/karts/controller/arena_ai.hpp +++ b/src/karts/controller/arena_ai.hpp @@ -65,7 +65,8 @@ protected: bool m_mini_skid; - void collectItemInArena(Vec3*, int*) const; + void collectItemInArena(Vec3*, int*) const; + virtual void findClosestKart(bool use_difficulty, bool find_sta) = 0; private: /** Used by handleArenaUTurn, it tells whether to do left or right * turning when steering is overridden. */ @@ -119,7 +120,6 @@ private: void useItems(const float dt); virtual bool canSkid(float steer_fraction) OVERRIDE { return m_mini_skid; } - virtual void findClosestKart(bool use_difficulty) = 0; virtual void findTarget() = 0; virtual bool forceBraking() { return false; } virtual int getCurrentNode() const = 0; diff --git a/src/karts/controller/battle_ai.cpp b/src/karts/controller/battle_ai.cpp index e5106fdfd..c2278e9c4 100644 --- a/src/karts/controller/battle_ai.cpp +++ b/src/karts/controller/battle_ai.cpp @@ -24,6 +24,7 @@ #include "items/powerup.hpp" #include "karts/abstract_kart.hpp" #include "karts/controller/kart_control.hpp" +#include "karts/controller/spare_tire_ai.hpp" #include "modes/three_strikes_battle.hpp" #include "tracks/arena_graph.hpp" @@ -65,36 +66,42 @@ BattleAI::~BattleAI() } // ~BattleAI //----------------------------------------------------------------------------- -void BattleAI::findClosestKart(bool use_difficulty) +void BattleAI::findClosestKart(bool use_difficulty, bool find_sta) { float distance = 99999.9f; - const unsigned int n = m_world->getNumKarts(); int closest_kart_num = 0; + const int end = m_world->getNumKarts(); - for (unsigned int i = 0; i < n; i++) + for (int start_id = + find_sta ? end - race_manager->getNumSpareTireKarts() : 0; + start_id < end; start_id++) { - const AbstractKart* kart = m_world->getKart(i); - if (kart->isEliminated()) continue; + const AbstractKart* kart = m_world->getKart(start_id); + const SpareTireAI* sta = + dynamic_cast(kart->getController()); + if (kart->isEliminated() && !(find_sta && sta && sta->isMoving())) + continue; if (kart->getWorldKartId() == m_kart->getWorldKartId()) continue; // Skip the same kart // Test whether takes current difficulty into account for closest kart // Notice: it don't affect aiming, this function will be called once - // more in handleArenaItems, which ignore difficulty. + // more when use items, which ignore difficulty. if (m_cur_difficulty == RaceManager::DIFFICULTY_EASY && use_difficulty) { - // Skip human players for novice mode unless only human players left - const AbstractKart* temp = m_world->getKart(i); + // Skip human players for novice mode unless only they are left + const AbstractKart* temp = m_world->getKart(start_id); if (temp->getController()->isPlayerController() && (m_world->getCurrentNumKarts() - m_world->getCurrentNumPlayers()) > 1) continue; } - else if (m_cur_difficulty == RaceManager::DIFFICULTY_BEST && use_difficulty) + else if (m_cur_difficulty == RaceManager::DIFFICULTY_BEST && + use_difficulty) { // Skip AI players for supertux mode - const AbstractKart* temp = m_world->getKart(i); + const AbstractKart* temp = m_world->getKart(start_id); if (!(temp->getController()->isPlayerController())) continue; } @@ -104,7 +111,7 @@ void BattleAI::findClosestKart(bool use_difficulty) if (dist_to_kart <= distance) { distance = dist_to_kart; - closest_kart_num = i; + closest_kart_num = start_id; } } @@ -117,9 +124,30 @@ void BattleAI::findClosestKart(bool use_difficulty) //----------------------------------------------------------------------------- void BattleAI::findTarget() { + // Find the closest kart first, it's used as fallback if no item is found. + // It takes the current difficulty into account, also collect life from + // spare tire karts when neccessary + + // Collect life depends on current difficulty: + // Novice and intermediate - collect them only AI has 1 life only + // Expert and supertux - collect them if AI dones't have 3 lives + // Also when actually spare tire karts are spawned + bool find_sta = m_world->spareTireKartsSpawned() ? + ((m_cur_difficulty == RaceManager::DIFFICULTY_EASY || + m_cur_difficulty == RaceManager::DIFFICULTY_MEDIUM) && + m_world->getKartLife(m_kart->getWorldKartId()) == 1 ? + true : + (m_cur_difficulty == RaceManager::DIFFICULTY_HARD || + m_cur_difficulty == RaceManager::DIFFICULTY_BEST) && + m_world->getKartLife(m_kart->getWorldKartId()) != 3 ? + true : false) : false; + + findClosestKart(find_sta ? false : true/*use_difficulty*/, find_sta); + // Find a suitable target to drive to, either powerup or kart if (m_kart->getPowerup()->getType() == PowerupManager::POWERUP_NOTHING && - m_kart->getAttachment()->getType() != Attachment::ATTACH_SWATTER) + m_kart->getAttachment()->getType() != Attachment::ATTACH_SWATTER && + !find_sta) collectItemInArena(&m_target_point , &m_target_node); else { diff --git a/src/karts/controller/battle_ai.hpp b/src/karts/controller/battle_ai.hpp index d95e1144c..915f34d15 100644 --- a/src/karts/controller/battle_ai.hpp +++ b/src/karts/controller/battle_ai.hpp @@ -33,9 +33,9 @@ class BattleAI : public ArenaAI protected: /** Keep a pointer to world. */ ThreeStrikesBattle *m_world; + virtual void findClosestKart(bool use_difficulty, bool find_sta) OVERRIDE; virtual int getCurrentNode() const OVERRIDE; private: - virtual void findClosestKart(bool use_difficulty) OVERRIDE; virtual void findTarget() OVERRIDE; virtual float getKartDistance(const AbstractKart* kart) const OVERRIDE; virtual bool isKartOnRoad() const OVERRIDE; diff --git a/src/karts/controller/soccer_ai.cpp b/src/karts/controller/soccer_ai.cpp index 5caf323be..af4e6925a 100644 --- a/src/karts/controller/soccer_ai.cpp +++ b/src/karts/controller/soccer_ai.cpp @@ -129,7 +129,7 @@ void SoccerAI::update(float dt) } // update //----------------------------------------------------------------------------- -void SoccerAI::findClosestKart(bool use_difficulty) +void SoccerAI::findClosestKart(bool use_difficulty, bool find_sta) { float distance = 99999.9f; const unsigned int n = m_world->getNumKarts(); @@ -165,6 +165,7 @@ void SoccerAI::findClosestKart(bool use_difficulty) //----------------------------------------------------------------------------- void SoccerAI::findTarget() { + findClosestKart(true/*use_difficulty*/, false/*find_sta*/); // Check if this AI kart is the one who will chase the ball if (m_world->getBallChaser(m_cur_team) == (signed)m_kart->getWorldKartId()) { diff --git a/src/karts/controller/soccer_ai.hpp b/src/karts/controller/soccer_ai.hpp index b9a3bcd1d..a93832781 100644 --- a/src/karts/controller/soccer_ai.hpp +++ b/src/karts/controller/soccer_ai.hpp @@ -65,7 +65,7 @@ private: virtual bool canSkid(float steer_fraction) OVERRIDE { return m_mini_skid && !(m_overtake_ball || m_chasing_ball); } - virtual void findClosestKart(bool use_difficulty) OVERRIDE; + virtual void findClosestKart(bool use_difficulty, bool find_sta) OVERRIDE; virtual void findTarget() OVERRIDE; virtual bool forceBraking() OVERRIDE { return m_force_brake; } virtual int getCurrentNode() const OVERRIDE; diff --git a/src/karts/controller/spare_tire_ai.hpp b/src/karts/controller/spare_tire_ai.hpp index e68aabb2b..d502a3d57 100644 --- a/src/karts/controller/spare_tire_ai.hpp +++ b/src/karts/controller/spare_tire_ai.hpp @@ -33,7 +33,6 @@ private: float m_timer; - virtual void findClosestKart(bool use_difficulty) OVERRIDE {} virtual void findTarget() OVERRIDE; void findDefaultPath(); public: @@ -43,7 +42,7 @@ public: virtual void reset() OVERRIDE; void spawn(float time_to_last); void unspawn(); - bool needUpdate() const { return !m_fixed_target_nodes.empty(); } + bool isMoving() const { return !m_fixed_target_nodes.empty(); } }; #endif diff --git a/src/modes/three_strikes_battle.cpp b/src/modes/three_strikes_battle.cpp index f5c06d996..834d85415 100644 --- a/src/modes/three_strikes_battle.cpp +++ b/src/modes/three_strikes_battle.cpp @@ -571,7 +571,7 @@ void ThreeStrikesBattle::enterRaceOverState() SpareTireAI* sta = dynamic_cast(m_spare_tire_karts[i]->getController()); assert(sta); - if (sta->needUpdate()) + if (sta->isMoving()) sta->unspawn(); } @@ -597,7 +597,7 @@ bool ThreeStrikesBattle::spareTireKartsSpawned() const dynamic_cast(m_spare_tire_karts[0]->getController()); assert(sta); - return sta->needUpdate(); + return sta->isMoving(); } // spareTireKartsSpawned //----------------------------------------------------------------------------- diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 8cb7e918c..57590aa8e 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -981,7 +981,7 @@ void World::update(float dt) SpareTireAI* sta = dynamic_cast(m_karts[i]->getController()); // Update all karts that are not eliminated - if(!m_karts[i]->isEliminated() || (sta && sta->needUpdate())) + if(!m_karts[i]->isEliminated() || (sta && sta->isMoving())) m_karts[i]->update(dt); } PROFILER_POP_CPU_MARKER(); diff --git a/src/modes/world_with_rank.cpp b/src/modes/world_with_rank.cpp index e03835954..60f4ffe27 100644 --- a/src/modes/world_with_rank.cpp +++ b/src/modes/world_with_rank.cpp @@ -254,7 +254,7 @@ void WorldWithRank::updateSectorForKarts() { SpareTireAI* sta = dynamic_cast(m_karts[i]->getController()); - if (!m_karts[i]->isEliminated() || (sta && sta->needUpdate())) + if (!m_karts[i]->isEliminated() || (sta && sta->isMoving())) getTrackSector(i)->update(m_karts[i]->getXYZ()); } } // updateSectorForKarts diff --git a/src/states_screens/race_gui.cpp b/src/states_screens/race_gui.cpp index d09270531..8012947ef 100644 --- a/src/states_screens/race_gui.cpp +++ b/src/states_screens/race_gui.cpp @@ -379,7 +379,7 @@ void RaceGUI::drawGlobalMiniMap() const SpareTireAI* sta = dynamic_cast(kart->getController()); // don't draw eliminated kart - if(kart->isEliminated() && !(sta && sta->needUpdate())) continue; + if(kart->isEliminated() && !(sta && sta->isMoving())) continue; const Vec3& xyz = kart->getXYZ(); Vec3 draw_at; world->getTrack()->mapPoint2MiniMap(xyz, &draw_at); From b4907f5519bbeb75a71b9dbf58d8932c24b27de0 Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 9 Oct 2016 13:52:24 +0800 Subject: [PATCH 288/350] Tell player that they have max 3 lives only --- src/karts/controller/spare_tire_ai.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/karts/controller/spare_tire_ai.cpp b/src/karts/controller/spare_tire_ai.cpp index 91dc8ee18..abdaa8697 100644 --- a/src/karts/controller/spare_tire_ai.cpp +++ b/src/karts/controller/spare_tire_ai.cpp @@ -22,6 +22,7 @@ #include "karts/kart_gfx.hpp" #include "karts/max_speed.hpp" #include "modes/three_strikes_battle.hpp" +#include "states_screens/race_gui.hpp" #include "tracks/arena_graph.hpp" #include "tracks/arena_node.hpp" #include "physics/physics.hpp" @@ -129,8 +130,13 @@ void SpareTireAI::crashed(const AbstractKart *k) // Nothing happen when two spare tire karts crash each other if (dynamic_cast(k->getController()) != NULL) return; - // Max 3 lives only - if (m_world->getKartLife(k->getWorldKartId()) == 3) return; + // Tell player that they have max 3 lives only + if (m_world->getKartLife(k->getWorldKartId()) == 3) + { + World::getWorld()->getRaceGUI()->addMessage + (_("You can only have max 3 lives!"), k, 2.0f); + return; + } // Otherwise increase one life for that kart and unspawn m_world->addKartLife(k->getWorldKartId()); From fdcb4dac8d115f88e90c0e30e1f033eaf89d02ce Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 9 Oct 2016 17:41:20 +0800 Subject: [PATCH 289/350] Fix assertion when no spare tire karts --- src/modes/three_strikes_battle.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modes/three_strikes_battle.cpp b/src/modes/three_strikes_battle.cpp index 834d85415..d8b08f7c7 100644 --- a/src/modes/three_strikes_battle.cpp +++ b/src/modes/three_strikes_battle.cpp @@ -591,8 +591,9 @@ void ThreeStrikesBattle::enterRaceOverState() //----------------------------------------------------------------------------- bool ThreeStrikesBattle::spareTireKartsSpawned() const { + if (m_spare_tire_karts.empty()) return false; + // Spare tire karts are spawned if at least 1 of them needs update - assert(!m_spare_tire_karts.empty()); SpareTireAI* sta = dynamic_cast(m_spare_tire_karts[0]->getController()); assert(sta); From 4ed599403ea7815366bdc5a272ee3f92cdf83534 Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 10 Oct 2016 08:47:10 +0800 Subject: [PATCH 290/350] Show a message when a life gained --- src/karts/controller/spare_tire_ai.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/karts/controller/spare_tire_ai.cpp b/src/karts/controller/spare_tire_ai.cpp index abdaa8697..0a9eb6ab5 100644 --- a/src/karts/controller/spare_tire_ai.cpp +++ b/src/karts/controller/spare_tire_ai.cpp @@ -140,6 +140,7 @@ void SpareTireAI::crashed(const AbstractKart *k) // Otherwise increase one life for that kart and unspawn m_world->addKartLife(k->getWorldKartId()); + World::getWorld()->getRaceGUI()->addMessage(_("+1 life."), k, 2.0f); unspawn(); } // crashed From 03f9fb35da5f0a7de625ef3435ea876632559ab6 Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 10 Oct 2016 13:00:18 +0800 Subject: [PATCH 291/350] Fix valgrind warning --- src/tracks/track_object_presentation.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tracks/track_object_presentation.cpp b/src/tracks/track_object_presentation.cpp index eebfa6ad4..4472d5885 100644 --- a/src/tracks/track_object_presentation.cpp +++ b/src/tracks/track_object_presentation.cpp @@ -959,6 +959,7 @@ TrackObjectPresentationLight::TrackObjectPresentationLight( scene::ISceneNode* parent) : TrackObjectPresentationSceneNode(xml_node) { + m_color.set(0); xml_node.get("color", &m_color); const video::SColorf colorf(m_color); From aaa0e11770330fcdf598d57f712ed3a001e9e28d Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 10 Oct 2016 13:00:37 +0800 Subject: [PATCH 292/350] Fix using uninitialized values when init() the first time --- src/karts/kart_model.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/karts/kart_model.cpp b/src/karts/kart_model.cpp index b14b050ed..9bc8ad12c 100644 --- a/src/karts/kart_model.cpp +++ b/src/karts/kart_model.cpp @@ -131,6 +131,7 @@ KartModel::KartModel(bool is_master) m_min_suspension[i] = -0.07f; m_max_suspension[i] = 0.20f; m_dampen_suspension_amplitude[i] = 2.5f; + m_default_physics_suspension[i] = 0.25f; } m_wheel_filename[0] = ""; m_wheel_filename[1] = ""; @@ -861,17 +862,18 @@ void KartModel::update(float dt, float distance, float steer, float speed, float suspension_length = 0.0f; GhostKart* gk = dynamic_cast(m_kart); - // Prevent using m_default_physics_suspension uninitialized - if (gk && gt_replay_index == -1) break; - - if (gk) + // Prevent using suspension length uninitialized + if (dt != 0.0f && !(gk && gt_replay_index == -1)) { - suspension_length = gk->getSuspensionLength(gt_replay_index, i); - } - else - { - suspension_length = m_kart->getVehicle()->getWheelInfo(i). - m_raycastInfo.m_suspensionLength; + if (gk) + { + suspension_length = gk->getSuspensionLength(gt_replay_index, i); + } + else + { + suspension_length = m_kart->getVehicle()->getWheelInfo(i). + m_raycastInfo.m_suspensionLength; + } } // Check documentation of Kart::updateGraphics for the following line From d39f5e487b744614f9e022360f84d2150a98556e Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 10 Oct 2016 13:00:54 +0800 Subject: [PATCH 293/350] Don't set position and target of camera the same Otherwise NAN will be calculated in ViewArea of camera --- src/graphics/camera.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/graphics/camera.cpp b/src/graphics/camera.cpp index e374c282b..61da2c56d 100644 --- a/src/graphics/camera.cpp +++ b/src/graphics/camera.cpp @@ -245,8 +245,11 @@ void Camera::setMode(Mode mode) { Vec3 start_offset(0, 1.6f, -3); Vec3 current_position = m_kart->getTrans()(start_offset); + Vec3 target_position = m_kart->getTrans()(Vec3(0, 0, 1)); + // Don't set position and target the same, otherwise + // nan values will be calculated in ViewArea of camera m_camera->setPosition(current_position.toIrrVector()); - m_camera->setTarget(m_camera->getPosition()); + m_camera->setTarget(target_position.toIrrVector()); } m_mode = mode; @@ -291,7 +294,8 @@ void Camera::setInitialTransform() // direction till smoothMoveCamera has corrected this. Setting target // to position doesn't make sense, but smoothMoves will adjust the // value before the first frame is rendered - m_camera->setTarget(m_camera->getPosition()); + Vec3 target_position = m_kart->getTrans()(Vec3(0, 0, 1)); + m_camera->setTarget(target_position.toIrrVector()); m_camera->setRotation(core::vector3df(0, 0, 0)); m_camera->setRotation( core::vector3df( 0.0f, 0.0f, 0.0f ) ); m_camera->setFOV(m_fov); From 8e2d7c549917b562d54d564ec2784b4f874a6cf0 Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 10 Oct 2016 13:01:18 +0800 Subject: [PATCH 294/350] Fix memory leaks and uninitialized values of ShadowMatrices --- src/graphics/shadow_matrices.cpp | 44 ++++++++++++++++++++++---------- src/graphics/shadow_matrices.hpp | 6 ++--- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/graphics/shadow_matrices.cpp b/src/graphics/shadow_matrices.cpp index 260cf28ba..8b6e5d275 100644 --- a/src/graphics/shadow_matrices.cpp +++ b/src/graphics/shadow_matrices.cpp @@ -126,15 +126,34 @@ ShadowMatrices::ShadowMatrices() m_shadow_cam_nodes[1] = NULL; m_shadow_cam_nodes[2] = NULL; m_shadow_cam_nodes[3] = NULL; + m_rsm_map_available = false; + m_rsm_matrix_initialized = false; } // ShadowMatrices +// ---------------------------------------------------------------------------- +ShadowMatrices::~ShadowMatrices() +{ + resetShadowCamNodes(); + m_sun_cam->drop(); +} // ~ShadowMatrices +// ---------------------------------------------------------------------------- +void ShadowMatrices::resetShadowCamNodes() +{ + for (unsigned i = 0; i < 4; i++) + { + if (m_shadow_cam_nodes[i]) + { + m_shadow_cam_nodes[i]->drop(); + m_shadow_cam_nodes[i] = NULL; + } + } +} // resetShadowCamNodes + // ---------------------------------------------------------------------------- void ShadowMatrices::addLight(const core::vector3df &pos) { m_sun_cam->setPosition(pos); m_sun_cam->updateAbsolutePosition(); - m_rsm_matrix_initialized = false; - } // addLight // ---------------------------------------------------------------------------- @@ -334,17 +353,6 @@ void ShadowMatrices::computeMatrixesAndCameras(scene::ICameraSceneNode *const ca if (World::getWorld() && World::getWorld()->getTrack()) { - // Compute track extent - btVector3 btmin, btmax; - if (World::getWorld()->getTrack()->getPtrTriangleMesh()) - { - World::getWorld()->getTrack()->getTriangleMesh().getCollisionShape() - .getAabb(btTransform::getIdentity(), btmin, btmax); - } - const Vec3 vmin = btmin, vmax = btmax; - core::aabbox3df trackbox(vmin.toIrrVector(), vmax.toIrrVector() - - core::vector3df(0, 30, 0)); - float FarValues[] = { ShadowMatrices::m_shadow_split[1], @@ -411,8 +419,16 @@ void ShadowMatrices::computeMatrixesAndCameras(scene::ICameraSceneNode *const ca } // Rsm Matrix and camera - if (!m_rsm_matrix_initialized) + if (!m_rsm_matrix_initialized && + World::getWorld()->getTrack()->getPtrTriangleMesh()) { + // Compute track extent + Vec3 vmin, vmax; + World::getWorld()->getTrack()->getTriangleMesh().getCollisionShape() + .getAabb(btTransform::getIdentity(), vmin, vmax); + core::aabbox3df trackbox(vmin.toIrrVector(), vmax.toIrrVector() - + core::vector3df(0, 30, 0)); + if (trackbox.MinEdge.X != trackbox.MaxEdge.X && trackbox.MinEdge.Y != trackbox.MaxEdge.Y && // Cover the case where sun_cam_view_matrix is null diff --git a/src/graphics/shadow_matrices.hpp b/src/graphics/shadow_matrices.hpp index ad64374a7..1056988b0 100644 --- a/src/graphics/shadow_matrices.hpp +++ b/src/graphics/shadow_matrices.hpp @@ -61,6 +61,7 @@ private: public: ShadowMatrices(); + ~ShadowMatrices(); void computeMatrixesAndCameras(scene::ICameraSceneNode *const camnode, unsigned int width, unsigned int height); @@ -69,10 +70,7 @@ public: void renderShadowsDebug(); // ------------------------------------------------------------------------ - void resetShadowCamNodes() - { - memset(m_shadow_cam_nodes, 0, 4 * sizeof(void*)); - } // resetShadowCamNodes + void resetShadowCamNodes(); // ------------------------------------------------------------------------ scene::ICameraSceneNode** getShadowCamNodes() { From d1c1435b55f08a6c4fd5efe2afd76082c987746e Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 10 Oct 2016 05:16:19 +0000 Subject: [PATCH 295/350] Use a better string --- src/karts/controller/spare_tire_ai.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/karts/controller/spare_tire_ai.cpp b/src/karts/controller/spare_tire_ai.cpp index 0a9eb6ab5..fe95d8032 100644 --- a/src/karts/controller/spare_tire_ai.cpp +++ b/src/karts/controller/spare_tire_ai.cpp @@ -134,7 +134,7 @@ void SpareTireAI::crashed(const AbstractKart *k) if (m_world->getKartLife(k->getWorldKartId()) == 3) { World::getWorld()->getRaceGUI()->addMessage - (_("You can only have max 3 lives!"), k, 2.0f); + (_("You can have at most 3 lives!"), k, 2.0f); return; } From 1a9a4c626e50644852618b2f1811d59c9d26be38 Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 10 Oct 2016 14:38:30 +0800 Subject: [PATCH 296/350] Properly add the model view widget Use add() will set angle = 0 in ModelViewWidget --- src/states_screens/soccer_setup_screen.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/states_screens/soccer_setup_screen.cpp b/src/states_screens/soccer_setup_screen.cpp index a1331742d..7bdee6038 100644 --- a/src/states_screens/soccer_setup_screen.cpp +++ b/src/states_screens/soccer_setup_screen.cpp @@ -39,7 +39,7 @@ using namespace GUIEngine; DEFINE_SCREEN_SINGLETON( SoccerSetupScreen ); -#define KART_CONTINUOUS_ROTATION_SPEED 35.f +#define KART_CONTINUOUS_ROTATION_SPEED 20.f #define KART_CONFIRMATION_ROTATION_SPEED 4.f #define KART_CONFIRMATION_TARGET_ANGLE 10.f @@ -137,7 +137,7 @@ void SoccerSetupScreen::beforeAddingWidget() kart_view->m_y = 0; kart_view->m_w = 200; kart_view->m_h = 200; - kart_view->clearModels(); + kart_view->add(); // Record info about it for further update KartViewInfo info; From 6750fb3c94adef98ca37cb6ad41d5a450b03a6f4 Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 11 Oct 2016 00:11:45 +0800 Subject: [PATCH 297/350] Fix leaking of script engine --- src/modes/world.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 392d7ee83..a11f454be 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -485,6 +485,8 @@ World::~World() if(m_physics) delete m_physics; + delete m_script_engine; + m_world = NULL; irr_driver->getSceneManager()->clear(); From 850b23db1f501fef812e1025b056bdb2a4757789 Mon Sep 17 00:00:00 2001 From: Deve Date: Mon, 10 Oct 2016 22:47:30 +0200 Subject: [PATCH 298/350] Fixed setting viewport in multiplayer games. It was broken in commit 5cfed1bc1caed71afd03c652dafbb7a5f1c4ae0d. We mix irr_driver->getVideoDriver()->setViewPort() and glViewport(...) functions and thus the first one, which is executed during camera activation, in some cases doesn't work. It compares the viewport to values which has been set last time and thinks that nothing changed. It only happens on single player (with single camera), so we just restore the viewport to fullscreen after scene rendering to workaround the issue. Actually the problem was that it was set after camera activation, which overwrote proper values. I also fixed SSAO, RSM and normals vizualization in multiplayer games. --- src/graphics/render.cpp | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/graphics/render.cpp b/src/graphics/render.cpp index 3ef16eefc..a6aaeaaca 100644 --- a/src/graphics/render.cpp +++ b/src/graphics/render.cpp @@ -260,19 +260,32 @@ void IrrDriver::renderGLSL(float dt) { bool isRace = StateManager::get()->getGameState() == GUIEngine::GAME; FrameBuffer *fbo = m_post_processing->render(camnode, isRace); - + + // The viewport has been changed using glViewport function directly + // during scene rendering, but irrlicht thinks that nothing changed + // when single camera is used. In this case we set the viewport + // to whole screen manually. + glViewport(0, 0, irr_driver->getActualScreenSize().Width, + irr_driver->getActualScreenSize().Height); + if (irr_driver->getNormals()) - irr_driver->getFBO(FBO_NORMAL_AND_DEPTHS).BlitToDefault(viewport.UpperLeftCorner.X, viewport.UpperLeftCorner.Y, viewport.LowerRightCorner.X, viewport.LowerRightCorner.Y); + { + irr_driver->getFBO(FBO_NORMAL_AND_DEPTHS).BlitToDefault( + viewport.UpperLeftCorner.X, + irr_driver->getActualScreenSize().Height - viewport.LowerRightCorner.Y, + viewport.LowerRightCorner.X, + irr_driver->getActualScreenSize().Height - viewport.UpperLeftCorner.Y); + } else if (irr_driver->getSSAOViz()) { glBindFramebuffer(GL_FRAMEBUFFER, 0); - glViewport(viewport.UpperLeftCorner.X, viewport.UpperLeftCorner.Y, viewport.LowerRightCorner.X, viewport.LowerRightCorner.Y); + camera->activate(); m_post_processing->renderPassThrough(m_rtts->getFBO(FBO_HALF1_R).getRTT()[0], viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X, viewport.LowerRightCorner.Y - viewport.UpperLeftCorner.Y); } else if (irr_driver->getRSM()) { glBindFramebuffer(GL_FRAMEBUFFER, 0); - glViewport(viewport.UpperLeftCorner.X, viewport.UpperLeftCorner.Y, viewport.LowerRightCorner.X, viewport.LowerRightCorner.Y); + camera->activate(); m_post_processing->renderPassThrough(m_rtts->getRSM().getRTT()[0], viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X, viewport.LowerRightCorner.Y - viewport.UpperLeftCorner.Y); } else if (irr_driver->getShadowViz()) @@ -283,9 +296,7 @@ void IrrDriver::renderGLSL(float dt) { glEnable(GL_FRAMEBUFFER_SRGB); glBindFramebuffer(GL_FRAMEBUFFER, 0); - if (CVS->isDefferedEnabled()) - camera->activate(); - glViewport(viewport.UpperLeftCorner.X, viewport.UpperLeftCorner.Y, viewport.LowerRightCorner.X, viewport.LowerRightCorner.Y); + camera->activate(); m_post_processing->renderPassThrough(fbo->getRTT()[0], viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X, viewport.LowerRightCorner.Y - viewport.UpperLeftCorner.Y); glDisable(GL_FRAMEBUFFER_SRGB); } From 09ce5515b02f46107bbb885c1812f30d3ffc9bc0 Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 1 Apr 2016 08:51:39 +1100 Subject: [PATCH 299/350] Avoid extrapolation by making sure the client starts after receiving a message from the server, and only updating the previous position if the new previous position is indeed before the current client time. Fixed conflicts, removed dumb-client related interpolation code, left client starting in place. --- src/modes/world_status.cpp | 75 +++++++++++++++---- src/modes/world_status.hpp | 26 +++++-- .../protocols/game_events_protocol.cpp | 60 +++++++++++---- .../protocols/game_events_protocol.hpp | 7 +- .../protocols/kart_update_protocol.cpp | 4 +- src/network/race_event_manager.cpp | 15 ++++ src/network/race_event_manager.hpp | 4 +- 7 files changed, 153 insertions(+), 38 deletions(-) diff --git a/src/modes/world_status.cpp b/src/modes/world_status.cpp index c863caa0c..7dbafd2ed 100644 --- a/src/modes/world_status.cpp +++ b/src/modes/world_status.cpp @@ -26,6 +26,8 @@ #include "guiengine/modaldialog.hpp" #include "karts/abstract_kart.hpp" #include "modes/world.hpp" +#include "network/network_config.hpp" +#include "network/race_event_manager.hpp" #include "tracks/track.hpp" #include @@ -56,6 +58,8 @@ WorldStatus::WorldStatus() m_play_track_intro_sound = UserConfigParams::m_music; m_play_ready_set_go_sounds = true; + m_play_racestart_sounds = true; + m_server_is_ready = false; IrrlichtDevice *device = irr_driver->getDevice(); @@ -190,7 +194,7 @@ void WorldStatus::updateTime(const float dt) World::getWorld()->getWeather()->playSound(); } - return; + return; // Do not increase time case TRACK_INTRO_PHASE: m_auxiliary_timer += dt; @@ -209,7 +213,7 @@ void WorldStatus::updateTime(const float dt) // after 3.5 seconds. if (m_track_intro_sound->getStatus() == SFXBase::SFX_PLAYING && m_auxiliary_timer < 3.5f) - return; + return; // Do not increase time // Wait before ready phase if sounds are disabled if (!UserConfigParams::m_sfx && m_auxiliary_timer < 3.0f) @@ -219,7 +223,7 @@ void WorldStatus::updateTime(const float dt) { startEngines(); if (m_auxiliary_timer < 3.0f) - return; + return; // Do not increase time } m_auxiliary_timer = 0.0f; @@ -227,11 +231,40 @@ void WorldStatus::updateTime(const float dt) if (m_play_ready_set_go_sounds) m_prestart_sound->play(); - m_phase = READY_PHASE; - + // In a networked game the client needs to wait for a notification + // from the server that ready-set-go can start now. So client will go + // to the 'wait_for_server_phase', from which they will progress once + // the notification is received. In all other cases (no networking or + // server), immediately go to race start + if(!NetworkConfig::get()->isNetworking() || + NetworkConfig::get()->isServer() ) + { + // Notify the clients that they can start ready-set-go + if(NetworkConfig::get()->isServer()) + RaceEventManager::getInstance()->startReadySetGo(); + m_server_is_ready = true; + m_phase = WAIT_FOR_SERVER_PHASE; + } // if not networked + else if(NetworkConfig::get()->isNetworking()) + { + // must be client now + m_phase = WAIT_FOR_SERVER_PHASE; + } + return; // Don't increase time + case WAIT_FOR_SERVER_PHASE: + // On a client this phase waits for the server to be ready. On a + // server or in case of non-networked game this phase is reached + // with m_server_is_ready set to true, so it will immediately + // start the engines and then the race + if(!m_server_is_ready) return; + + m_phase = READY_PHASE; startEngines(); - break; + // Receiving a 'startReadySetGo' message from the server triggers + // a call to startReadySetGo() here, which will change the phase + // (or state) of the finite state machine. + return; // Don't increase time case READY_PHASE: if (m_auxiliary_timer > 1.0) { @@ -245,7 +278,8 @@ void WorldStatus::updateTime(const float dt) m_auxiliary_timer += dt; - // In artist debug mode, when without opponents, skip the ready/set/go counter faster + // In artist debug mode, when without opponents, skip the + // ready/set/go counter faster if (UserConfigParams::m_artist_debug_mode && race_manager->getNumberOfKarts() == 1 && race_manager->getTrackName() != "tutorial") @@ -253,7 +287,7 @@ void WorldStatus::updateTime(const float dt) m_auxiliary_timer += dt*6; } - return; + return; // Do not increase time case SET_PHASE: if (m_auxiliary_timer > 2.0) { @@ -270,7 +304,8 @@ void WorldStatus::updateTime(const float dt) m_auxiliary_timer += dt; - // In artist debug mode, when without opponents, skip the ready/set/go counter faster + // In artist debug mode, when without opponents, + // skip the ready/set/go counter faster if (UserConfigParams::m_artist_debug_mode && race_manager->getNumberOfKarts() == 1 && race_manager->getTrackName() != "tutorial") @@ -278,7 +313,7 @@ void WorldStatus::updateTime(const float dt) m_auxiliary_timer += dt*6; } - return; + return; // Do not increase time case GO_PHASE : if (m_auxiliary_timer>2.5f && music_manager->getCurrentMusic() && @@ -294,7 +329,8 @@ void WorldStatus::updateTime(const float dt) m_auxiliary_timer += dt; - // In artist debug mode, when without opponents, skip the ready/set/go counter faster + // In artist debug mode, when without opponents, + // skip the ready/set/go counter faster if (UserConfigParams::m_artist_debug_mode && race_manager->getNumberOfKarts() == 1 && race_manager->getTrackName() != "tutorial") @@ -302,7 +338,7 @@ void WorldStatus::updateTime(const float dt) m_auxiliary_timer += dt*6; } - break; + break; // Now the world time starts case MUSIC_PHASE: // Start the music here when starting fast if (UserConfigParams::m_race_now) @@ -347,7 +383,8 @@ void WorldStatus::updateTime(const float dt) default: break; } - + Log::verbose("time", "%f %f %f phase %d", + m_time, dt, m_time+dt, m_phase); switch (m_clock_mode) { case CLOCK_CHRONO: @@ -374,9 +411,19 @@ void WorldStatus::updateTime(const float dt) break; default: break; - } + } // switch m_phase } // update +//----------------------------------------------------------------------------- +/** Called on the client when it receives a notification from the server that + * the server is now starting its ready-set-go phase. This function changes + * the state of the finite state machine to be ready. + */ +void WorldStatus::startReadySetGo() +{ + m_server_is_ready = true; +} // startReadySetGo + //----------------------------------------------------------------------------- /** Sets the time for the clock. * \param time New time to set. diff --git a/src/modes/world_status.hpp b/src/modes/world_status.hpp index 3e2e5fe3c..eb821d7c8 100644 --- a/src/modes/world_status.hpp +++ b/src/modes/world_status.hpp @@ -45,6 +45,10 @@ public: // Game setup, e.g. track loading SETUP_PHASE, + // Used in network games only: wait for the server to broadcast + // 'start'. This happens on a network client only + WAIT_FOR_SERVER_PHASE, + // 'Ready' is displayed READY_PHASE, @@ -82,7 +86,15 @@ public: //Goal scored phase GOAL_PHASE }; + protected: + /** Elasped/remaining time in seconds. */ + double m_time; + + /** If the start race should be played, disabled in cutscenes. */ + bool m_play_racestart_sounds; + +private: /** Sound to play at the beginning of a race, during which a * a camera intro of the track can be shown. */ SFXBase *m_track_intro_sound; @@ -91,12 +103,9 @@ protected: /** The third sound to be played in ready, set, go. */ SFXBase *m_start_sound; - /** - * Elasped/remaining time in seconds - */ - double m_time; + /** The clock mode: normal counting forwards, or countdown */ ClockType m_clock_mode; - +protected: bool m_play_track_intro_sound; bool m_play_ready_set_go_sounds; @@ -118,6 +127,12 @@ private: bool m_engines_started; void startEngines(); + /** In networked game the client must wait for the server to start 'ready set go' + * (to guarantee that the client's time is not ahead of the server), This flag + * indicates that the notification from the server was received, and that the + * client can go to 'ready' phase. */ + bool m_server_is_ready; + public: WorldStatus(); virtual ~WorldStatus(); @@ -125,6 +140,7 @@ public: virtual void reset(); virtual void updateTime(const float dt); virtual void update(float dt); + void startReadySetGo(); virtual void pause(Phase phase); virtual void unpause(); virtual void enterRaceOverState(); diff --git a/src/network/protocols/game_events_protocol.cpp b/src/network/protocols/game_events_protocol.cpp index 1bf40db47..b037958ac 100644 --- a/src/network/protocols/game_events_protocol.cpp +++ b/src/network/protocols/game_events_protocol.cpp @@ -39,7 +39,7 @@ bool GameEventsProtocol::notifyEvent(Event* event) if (event->getType() != EVENT_TYPE_MESSAGE) return true; NetworkString &data = event->data(); - if (data.size() < 5) // for token and type + if (data.size() < 1) // for token and type { Log::warn("GameEventsProtocol", "Too short message."); return true; @@ -56,6 +56,9 @@ bool GameEventsProtocol::notifyEvent(Event* event) collectedItem(data); break; case GE_KART_FINISHED_RACE: kartFinishedRace(data); break; + case GE_START_READY_SET_GO: + receivedReadySetGo(); break; + default: Log::warn("GameEventsProtocol", "Unkown message type."); break; @@ -101,18 +104,18 @@ void GameEventsProtocol::collectedItem(const NetworkString &data) { if (data.size() < 6) { - Log::warn("GameEventsProtocol", "Too short message."); - } - uint32_t item_id = data.getUInt32(); - uint8_t powerup_type = data.getUInt8(); - uint8_t kart_id = data.getUInt8(); - // now set the kart powerup - AbstractKart* kart = World::getWorld()->getKart(kart_id); - ItemManager::get()->collectedItem(ItemManager::get()->getItem(item_id), - kart, powerup_type); - Log::info("GameEventsProtocol", "Item %d picked by a player.", - powerup_type); - } // collectedItem + Log::warn("GameEventsProtocol", "collectedItem: Too short message."); + } + uint32_t item_id = data.getUInt32(); + uint8_t powerup_type = data.getUInt8(); + uint8_t kart_id = data.getUInt8(); + // now set the kart powerup + AbstractKart* kart = World::getWorld()->getKart(kart_id); + ItemManager::get()->collectedItem(ItemManager::get()->getItem(item_id), + kart, powerup_type); + Log::info("GameEventsProtocol", "Item %d picked by a player.", + powerup_type); +} // collectedItem // ---------------------------------------------------------------------------- /** This function is called from the server when a kart finishes a race. It @@ -137,8 +140,39 @@ void GameEventsProtocol::kartFinishedRace(AbstractKart *kart, float time) */ void GameEventsProtocol::kartFinishedRace(const NetworkString &ns) { + if (ns.size() < 5) // for token and type + { + Log::warn("GameEventsProtocol", "kartFinisheRace: Too short message."); + return; + } + uint8_t kart_id = ns.getUInt8(); float time = ns.getFloat(); World::getWorld()->getKart(kart_id)->finishedRace(time, /*from_server*/true); } // kartFinishedRace + +// ---------------------------------------------------------------------------- +/** This function is called on a server when the world starts the ready-set-go + * phase. It signals to all clients to do the same. + */ +void GameEventsProtocol::startReadySetGo() +{ + assert(NetworkConfig::get()->isServer()); + NetworkString *ns = getNetworkString(1); + ns->setSynchronous(true); + ns->addUInt8(GE_START_READY_SET_GO); + sendMessageToPeersChangingToken(ns, /*reliable*/true); + delete ns; +} // startReadySetGo + +// ---------------------------------------------------------------------------- +/** Called on the client when it receives the message that the server has + * started its ready-set-go. Signal to world that it can go to the next + * phase (ready phase) now. + */ +void GameEventsProtocol::receivedReadySetGo() +{ + assert(NetworkConfig::get()->isClient()); + World::getWorld()->startReadySetGo(); +} // receivedReadySetGo diff --git a/src/network/protocols/game_events_protocol.hpp b/src/network/protocols/game_events_protocol.hpp index eed98bc78..c847d5db1 100644 --- a/src/network/protocols/game_events_protocol.hpp +++ b/src/network/protocols/game_events_protocol.hpp @@ -11,8 +11,9 @@ class GameEventsProtocol : public Protocol { private: enum GameEventType { - GE_ITEM_COLLECTED = 0x01, - GE_KART_FINISHED_RACE = 0x02 + GE_START_READY_SET_GO = 0x01, + GE_ITEM_COLLECTED = 0x02, + GE_KART_FINISHED_RACE = 0x03 }; // GameEventType public: @@ -24,6 +25,8 @@ public: void collectedItem(const NetworkString &ns); void kartFinishedRace(AbstractKart *kart, float time); void kartFinishedRace(const NetworkString &ns); + void startReadySetGo(); + void receivedReadySetGo(); virtual void setup() OVERRIDE {}; virtual void update(float dt) OVERRIDE {}; virtual void asynchronousUpdate() OVERRIDE{} diff --git a/src/network/protocols/kart_update_protocol.cpp b/src/network/protocols/kart_update_protocol.cpp index 6c2f70b6e..c6bcb2c20 100644 --- a/src/network/protocols/kart_update_protocol.cpp +++ b/src/network/protocols/kart_update_protocol.cpp @@ -36,7 +36,9 @@ void KartUpdateProtocol::setup() */ bool KartUpdateProtocol::notifyEvent(Event* event) { - if (event->getType() != EVENT_TYPE_MESSAGE) + // It might be possible that we still receive messages after + // the game was exited, so make sure we still have a world. + if (event->getType() != EVENT_TYPE_MESSAGE || !World::getWorld()) return true; NetworkString &ns = event->data(); if (ns.size() < 33) diff --git a/src/network/race_event_manager.cpp b/src/network/race_event_manager.cpp index e967b2dd7..c4112ab20 100644 --- a/src/network/race_event_manager.cpp +++ b/src/network/race_event_manager.cpp @@ -100,6 +100,21 @@ void RaceEventManager::collectedItem(Item *item, AbstractKart *kart) protocol->collectedItem(item,kart); } // collectedItem +// ---------------------------------------------------------------------------- +/** A message from the server to all clients that it is now starting the + * 'ready' phase. Clients will wait for this event before they display + * RSG. This will make sure that the server time is always ahead of + * the client time. + */ +void RaceEventManager::startReadySetGo() +{ + // this is only called in the server + assert(NetworkConfig::get()->isServer()); + + GameEventsProtocol* protocol = static_cast( + ProtocolManager::getInstance()->getProtocol(PROTOCOL_GAME_EVENTS)); + protocol->startReadySetGo(); +} // startReadySetGo // ---------------------------------------------------------------------------- void RaceEventManager::controllerAction(Controller* controller, PlayerAction action, int value) diff --git a/src/network/race_event_manager.hpp b/src/network/race_event_manager.hpp index 1a21f433f..f224c2cff 100755 --- a/src/network/race_event_manager.hpp +++ b/src/network/race_event_manager.hpp @@ -16,9 +16,6 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -/*! \file network_world.hpp - */ - #ifndef NETWORK_WORLD_HPP #define NETWORK_WORLD_HPP @@ -59,6 +56,7 @@ public: void controllerAction(Controller* controller, PlayerAction action, int value); void kartFinishedRace(AbstractKart *kart, float time); + void startReadySetGo(); // ------------------------------------------------------------------------ /** Returns if this instance is in running state or not. */ bool isRunning() { return m_running; } From 7b3fc04d2688c5a5c1c793d19ca32e5937438f24 Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 1 Apr 2016 13:10:38 +1100 Subject: [PATCH 300/350] Removed debug output. --- src/modes/world_status.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/modes/world_status.cpp b/src/modes/world_status.cpp index 7dbafd2ed..b2119c566 100644 --- a/src/modes/world_status.cpp +++ b/src/modes/world_status.cpp @@ -383,8 +383,7 @@ void WorldStatus::updateTime(const float dt) default: break; } - Log::verbose("time", "%f %f %f phase %d", - m_time, dt, m_time+dt, m_phase); + switch (m_clock_mode) { case CLOCK_CHRONO: From 28f1e45f799d34663062c38ba7bc0e42f59a165b Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 11 Oct 2016 09:09:09 +0800 Subject: [PATCH 301/350] Really fix model view widget --- src/guiengine/widgets/model_view_widget.cpp | 33 ++++++++++----------- src/guiengine/widgets/model_view_widget.hpp | 2 +- src/states_screens/soccer_setup_screen.cpp | 4 +-- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/guiengine/widgets/model_view_widget.cpp b/src/guiengine/widgets/model_view_widget.cpp index c5722e54e..89205dd37 100644 --- a/src/guiengine/widgets/model_view_widget.cpp +++ b/src/guiengine/widgets/model_view_widget.cpp @@ -50,6 +50,7 @@ IconButtonWidget(IconButtonWidget::SCALE_MODE_KEEP_TEXTURE_ASPECT_RATIO, false, m_old_rtt_provider = NULL; m_rotation_mode = ROTATE_OFF; m_render_info = new RenderInfo(); + m_angle = 0; // so that the base class doesn't complain there is no icon defined m_properties[PROP_ICON]="gui/main_help.png"; @@ -85,8 +86,6 @@ void ModelViewWidget::add() */ GUIEngine::needsUpdate.push_back(this); - angle = 0; - } // add // ----------------------------------------------------------------------------- @@ -133,28 +132,28 @@ void ModelViewWidget::update(float delta) if (m_rotation_mode == ROTATE_CONTINUOUSLY) { - angle += delta*m_rotation_speed; - if (angle > 360) angle -= 360; + m_angle += delta*m_rotation_speed; + if (m_angle > 360) m_angle -= 360; } else if (m_rotation_mode == ROTATE_TO) { // check if we should rotate clockwise or counter-clockwise to reach the target faster // (taking wrap-arounds into account) - const int angle_distance_from_end = (int)(360 - angle); - const int target_distance_from_end = (int)(360 - angle); + const int angle_distance_from_end = (int)(360 - m_angle); + const int target_distance_from_end = (int)(360 - m_angle); int distance_with_positive_rotation; int distance_with_negative_rotation; - if (angle < m_rotation_target) + if (m_angle < m_rotation_target) { - distance_with_positive_rotation = (int)(m_rotation_target - angle); - distance_with_negative_rotation = (int)(angle + target_distance_from_end); + distance_with_positive_rotation = (int)(m_rotation_target - m_angle); + distance_with_negative_rotation = (int)(m_angle + target_distance_from_end); } else { distance_with_positive_rotation = (int)(angle_distance_from_end + m_rotation_target); - distance_with_negative_rotation = (int)(angle - m_rotation_target); + distance_with_negative_rotation = (int)(m_angle - m_rotation_target); } //Log::info("ModelViewWidget", "distance_with_positive_rotation = %d; " @@ -163,17 +162,17 @@ void ModelViewWidget::update(float delta) if (distance_with_positive_rotation < distance_with_negative_rotation) { - angle += m_rotation_speed * delta*(3.0f + std::min(distance_with_positive_rotation, distance_with_negative_rotation)*2.0f); + m_angle += m_rotation_speed * delta*(3.0f + std::min(distance_with_positive_rotation, distance_with_negative_rotation)*2.0f); } else { - angle -= m_rotation_speed * delta*(3.0f + std::min(distance_with_positive_rotation, distance_with_negative_rotation)*2.0f); + m_angle -= m_rotation_speed * delta*(3.0f + std::min(distance_with_positive_rotation, distance_with_negative_rotation)*2.0f); } - if (angle > 360) angle -= 360; - if (angle < 0) angle += 360; + if (m_angle > 360) m_angle -= 360; + if (m_angle < 0) m_angle += 360; // stop rotating when target reached - if (fabsf(angle - m_rotation_target) < 2.0f) m_rotation_mode = ROTATE_OFF; + if (fabsf(m_angle - m_rotation_target) < 2.0f) m_rotation_mode = ROTATE_OFF; } if (CVS->isGLSL()) @@ -188,7 +187,7 @@ void ModelViewWidget::update(float delta) setupRTTScene(); } - m_rtt_main_node->setRotation(core::vector3df(0.0f, angle, 0.0f)); + m_rtt_main_node->setRotation(core::vector3df(0.0f, m_angle, 0.0f)); m_rtt_main_node->setVisible(true); @@ -206,7 +205,7 @@ void ModelViewWidget::update(float delta) m_old_rtt_provider->setupRTTScene(m_models, m_model_location, m_model_scale, m_model_frames); } - m_texture = m_old_rtt_provider->renderToTexture(angle); + m_texture = m_old_rtt_provider->renderToTexture(m_angle); if (m_texture == NULL) { diff --git a/src/guiengine/widgets/model_view_widget.hpp b/src/guiengine/widgets/model_view_widget.hpp index 22b9082bc..472fba973 100644 --- a/src/guiengine/widgets/model_view_widget.hpp +++ b/src/guiengine/widgets/model_view_widget.hpp @@ -56,7 +56,7 @@ namespace GUIEngine RTT* m_rtt_provider; IrrDriver::RTTProvider* m_old_rtt_provider; - float angle; + float m_angle; bool m_rtt_unsupported; diff --git a/src/states_screens/soccer_setup_screen.cpp b/src/states_screens/soccer_setup_screen.cpp index 7bdee6038..a1331742d 100644 --- a/src/states_screens/soccer_setup_screen.cpp +++ b/src/states_screens/soccer_setup_screen.cpp @@ -39,7 +39,7 @@ using namespace GUIEngine; DEFINE_SCREEN_SINGLETON( SoccerSetupScreen ); -#define KART_CONTINUOUS_ROTATION_SPEED 20.f +#define KART_CONTINUOUS_ROTATION_SPEED 35.f #define KART_CONFIRMATION_ROTATION_SPEED 4.f #define KART_CONFIRMATION_TARGET_ANGLE 10.f @@ -137,7 +137,7 @@ void SoccerSetupScreen::beforeAddingWidget() kart_view->m_y = 0; kart_view->m_w = 200; kart_view->m_h = 200; - kart_view->add(); + kart_view->clearModels(); // Record info about it for further update KartViewInfo info; From fb79cf3a6e0438951cb85aaa74d8e87baff382cd Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 11 Oct 2016 10:52:08 +0800 Subject: [PATCH 302/350] Let spare tire karts follow bounding box points --- src/karts/controller/spare_tire_ai.cpp | 48 ++++++++++---------------- src/karts/controller/spare_tire_ai.hpp | 4 +-- src/tracks/arena_graph.cpp | 2 ++ src/tracks/drive_graph.cpp | 2 ++ src/tracks/graph.cpp | 14 ++++++++ src/tracks/graph.hpp | 12 +++++++ 6 files changed, 51 insertions(+), 31 deletions(-) diff --git a/src/karts/controller/spare_tire_ai.cpp b/src/karts/controller/spare_tire_ai.cpp index fe95d8032..18a9f5123 100644 --- a/src/karts/controller/spare_tire_ai.cpp +++ b/src/karts/controller/spare_tire_ai.cpp @@ -28,6 +28,8 @@ #include "physics/physics.hpp" #include "utils/random_generator.hpp" +#include + SpareTireAI::SpareTireAI(AbstractKart *kart) : BattleAI(kart) { @@ -36,6 +38,16 @@ SpareTireAI::SpareTireAI(AbstractKart *kart) // billboard showing 'AIBaseController' to the kart. Controller::setControllerName("SpareTireAI"); + // Pre-load the 4 nodes of bounding box defined by battle world + memcpy(m_fixed_target_nodes, m_graph->getBBNodes(), 4 * sizeof(int)); + + // Reverse the order depends on world ID, so not all spare tire karts go + // the same way + if (m_kart->getWorldKartId() % 2 != 0) + { + std::reverse(std::begin(m_fixed_target_nodes), + std::end(m_fixed_target_nodes)); + } } // SpareTireAI //----------------------------------------------------------------------------- @@ -44,7 +56,6 @@ SpareTireAI::SpareTireAI(AbstractKart *kart) void SpareTireAI::reset() { BattleAI::reset(); - m_fixed_target_nodes.clear(); m_idx = 0; m_timer = 0.0f; } // reset @@ -62,30 +73,11 @@ void SpareTireAI::update(float dt) //----------------------------------------------------------------------------- void SpareTireAI::findDefaultPath() { - // Randomly find 3 nodes for spare tire kart to move - assert(m_fixed_target_nodes.empty()); - const int nodes = m_graph->getNumNodes(); - const float min_dist = sqrtf(nodes); + // Randomly find a start node for spare tire kart to move + assert(m_idx == -1); + RandomGenerator random; - while (m_fixed_target_nodes.size() < 3) - { - int node = random.get(nodes); - if (m_fixed_target_nodes.empty()) - { - m_fixed_target_nodes.push_back(node); - continue; - } - bool succeed = true; - for (const int& all_node : m_fixed_target_nodes) - { - float dist = m_graph->getDistance(all_node, node); - if (dist < min_dist) - succeed = false; - } - if (succeed) - m_fixed_target_nodes.push_back(node); - } - m_idx = 0; + m_idx = random.get(4); m_target_node = m_fixed_target_nodes[m_idx]; } // findDefaultPath @@ -93,11 +85,9 @@ void SpareTireAI::findDefaultPath() //----------------------------------------------------------------------------- void SpareTireAI::findTarget() { - if (m_fixed_target_nodes.empty()) return; - - assert(m_fixed_target_nodes.size() == 3); + assert(m_idx != -1 && m_idx < 4); if (getCurrentNode() == m_fixed_target_nodes[m_idx]) - m_idx = m_idx == 2 ? 0 : m_idx + 1; + m_idx = m_idx == 3 ? 0 : m_idx + 1; const int chosen_node = m_fixed_target_nodes[m_idx]; m_target_node = chosen_node; @@ -120,7 +110,7 @@ void SpareTireAI::spawn(float time_to_last) //----------------------------------------------------------------------------- void SpareTireAI::unspawn() { - reset(); + m_idx = -1; m_kart->eliminate(); } // unspawn diff --git a/src/karts/controller/spare_tire_ai.hpp b/src/karts/controller/spare_tire_ai.hpp index d502a3d57..8aa7c7924 100644 --- a/src/karts/controller/spare_tire_ai.hpp +++ b/src/karts/controller/spare_tire_ai.hpp @@ -27,7 +27,7 @@ class SpareTireAI : public BattleAI { private: - std::vector m_fixed_target_nodes; + int m_fixed_target_nodes[4]; int m_idx; @@ -42,7 +42,7 @@ public: virtual void reset() OVERRIDE; void spawn(float time_to_last); void unspawn(); - bool isMoving() const { return !m_fixed_target_nodes.empty(); } + bool isMoving() const { return m_idx != -1; } }; #endif diff --git a/src/tracks/arena_graph.cpp b/src/tracks/arena_graph.cpp index 042d4d211..5ac41adcd 100644 --- a/src/tracks/arena_graph.cpp +++ b/src/tracks/arena_graph.cpp @@ -44,6 +44,8 @@ ArenaGraph::ArenaGraph(const std::string &navmesh, const XMLNode *node) if (node && race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER) loadGoalNodes(node); + loadBoundingBoxNodes(); + } // ArenaGraph // ----------------------------------------------------------------------------- diff --git a/src/tracks/drive_graph.cpp b/src/tracks/drive_graph.cpp index 5f98b1ad8..7f00e86dc 100644 --- a/src/tracks/drive_graph.cpp +++ b/src/tracks/drive_graph.cpp @@ -223,6 +223,8 @@ void DriveGraph::load(const std::string &quad_file_name, m_lap_length = l; } + loadBoundingBoxNodes(); + } // load // ---------------------------------------------------------------------------- diff --git a/src/tracks/graph.cpp b/src/tracks/graph.cpp index ff9dc9bea..de7aec504 100644 --- a/src/tracks/graph.cpp +++ b/src/tracks/graph.cpp @@ -46,6 +46,7 @@ Graph::Graph() m_new_rtt = NULL; m_bb_min = Vec3( 99999, 99999, 99999); m_bb_max = Vec3(-99999, -99999, -99999); + memset(m_bb_nodes, 0, 4 * sizeof(int)); } // Graph // ----------------------------------------------------------------------------- @@ -618,3 +619,16 @@ int Graph::findOutOfRoadSector(const Vec3& xyz, const int curr_sector, } return min_sector; } // findOutOfRoadSector + +//----------------------------------------------------------------------------- +void Graph::loadBoundingBoxNodes() +{ + m_bb_nodes[0] = findOutOfRoadSector(Vec3(m_bb_min.x(), 0, m_bb_min.z()), + -1/*curr_sector*/, NULL/*all_sectors*/, true/*ignore_vertical*/); + m_bb_nodes[1] = findOutOfRoadSector(Vec3(m_bb_min.x(), 0, m_bb_max.z()), + -1/*curr_sector*/, NULL/*all_sectors*/, true/*ignore_vertical*/); + m_bb_nodes[2] = findOutOfRoadSector(Vec3(m_bb_max.x(), 0, m_bb_min.z()), + -1/*curr_sector*/, NULL/*all_sectors*/, true/*ignore_vertical*/); + m_bb_nodes[3] = findOutOfRoadSector(Vec3(m_bb_max.x(), 0, m_bb_max.z()), + -1/*curr_sector*/, NULL/*all_sectors*/, true/*ignore_vertical*/); +} // loadBoundingBoxNodes diff --git a/src/tracks/graph.hpp b/src/tracks/graph.hpp index 8f713cb6e..cd7fccda8 100644 --- a/src/tracks/graph.hpp +++ b/src/tracks/graph.hpp @@ -62,12 +62,18 @@ protected: void createQuad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, unsigned int node_index, bool invisible, bool ai_ignore, bool is_arena); + // ------------------------------------------------------------------------ + /** Map 4 bounding box points to 4 closest graph nodes. */ + void loadBoundingBoxNodes(); private: /** The 2d bounding box, used for hashing. */ Vec3 m_bb_min; Vec3 m_bb_max; + /** The 4 closest graph nodes to the bounding box. */ + int m_bb_nodes[4]; + RTT* m_new_rtt; /** The node of the graph mesh. */ @@ -149,6 +155,12 @@ public: const int curr_sector = UNKNOWN_SECTOR, std::vector *all_sectors = NULL, bool ignore_vertical = false) const; + // ------------------------------------------------------------------------ + const Vec3& getBBMin() const { return m_bb_min; } + // ------------------------------------------------------------------------ + const Vec3& getBBMax() const { return m_bb_max; } + // ------------------------------------------------------------------------ + const int* getBBNodes() const { return m_bb_nodes; } }; // Graph From ef7201cf518b2b05ed8f3532f96a3e3cb61df650 Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 11 Oct 2016 12:24:36 +0800 Subject: [PATCH 303/350] Remove an unneeded line --- src/graphics/camera.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/graphics/camera.cpp b/src/graphics/camera.cpp index 61da2c56d..6cb61b53f 100644 --- a/src/graphics/camera.cpp +++ b/src/graphics/camera.cpp @@ -297,7 +297,6 @@ void Camera::setInitialTransform() Vec3 target_position = m_kart->getTrans()(Vec3(0, 0, 1)); m_camera->setTarget(target_position.toIrrVector()); m_camera->setRotation(core::vector3df(0, 0, 0)); - m_camera->setRotation( core::vector3df( 0.0f, 0.0f, 0.0f ) ); m_camera->setFOV(m_fov); } // setInitialTransform From 68f5259f4dedb6c7b42ac2aea015247e4959f95f Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 1 Apr 2016 16:15:37 +1100 Subject: [PATCH 304/350] Removed static variables and properly initialised in setup, which should allow this protocol to work for more than one race. Back-ported to master. --- .../protocols/kart_update_protocol.cpp | 24 ++++++++++--------- .../protocols/kart_update_protocol.hpp | 4 ++++ 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/network/protocols/kart_update_protocol.cpp b/src/network/protocols/kart_update_protocol.cpp index c6bcb2c20..dce04265e 100644 --- a/src/network/protocols/kart_update_protocol.cpp +++ b/src/network/protocols/kart_update_protocol.cpp @@ -10,14 +10,6 @@ KartUpdateProtocol::KartUpdateProtocol() : Protocol(PROTOCOL_KART_UPDATE) { - // Allocate arrays to store one position and rotation for each kart - // (which is the update information from the server to the client). - m_next_positions.resize(World::getWorld()->getNumKarts()); - m_next_quaternions.resize(World::getWorld()->getNumKarts()); - - // This flag keeps track if valid data for an update is in - // the arrays - m_was_updated = false; } // KartUpdateProtocol // ---------------------------------------------------------------------------- @@ -28,6 +20,16 @@ KartUpdateProtocol::~KartUpdateProtocol() // ---------------------------------------------------------------------------- void KartUpdateProtocol::setup() { + // Allocate arrays to store one position and rotation for each kart + // (which is the update information from the server to the client). + m_next_positions.resize(World::getWorld()->getNumKarts()); + m_next_quaternions.resize(World::getWorld()->getNumKarts()); + + // This flag keeps track if valid data for an update is in + // the arrays + m_was_updated = false; + + m_previous_time = 0; } // setup // ---------------------------------------------------------------------------- @@ -73,11 +75,11 @@ void KartUpdateProtocol::update(float dt) { if (!World::getWorld()) return; - static double time = 0; + double current_time = StkTime::getRealTime(); - if (current_time > time + 0.1) // 10 updates per second + if (current_time > m_previous_time + 0.1) // 10 updates per second { - time = current_time; + m_previous_time = current_time; if (NetworkConfig::get()->isServer()) { World *world = World::getWorld(); diff --git a/src/network/protocols/kart_update_protocol.hpp b/src/network/protocols/kart_update_protocol.hpp index be5e144f4..04d83f488 100644 --- a/src/network/protocols/kart_update_protocol.hpp +++ b/src/network/protocols/kart_update_protocol.hpp @@ -25,6 +25,10 @@ private: /** True if a new update for the kart positions was received. */ bool m_was_updated; + /** Time the last kart update was sent. Used to send updates with + * a fixed frequency. */ + double m_previous_time; + public: KartUpdateProtocol(); virtual ~KartUpdateProtocol(); From 3243f81f1094031888302437550b3092bc15bdd7 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 4 Apr 2016 18:16:33 +1000 Subject: [PATCH 305/350] Avoid crash. --- src/network/protocols/game_events_protocol.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/network/protocols/game_events_protocol.cpp b/src/network/protocols/game_events_protocol.cpp index b037958ac..9b0a619ed 100644 --- a/src/network/protocols/game_events_protocol.cpp +++ b/src/network/protocols/game_events_protocol.cpp @@ -36,7 +36,9 @@ GameEventsProtocol::~GameEventsProtocol() // ---------------------------------------------------------------------------- bool GameEventsProtocol::notifyEvent(Event* event) { - if (event->getType() != EVENT_TYPE_MESSAGE) + // Avoid crash in case that we still receive race events when + // the race is actually over. + if (event->getType() != EVENT_TYPE_MESSAGE || !World::getWorld()) return true; NetworkString &data = event->data(); if (data.size() < 1) // for token and type From b0bb6ce00f0877bc40eaffc4f25642e4fb742632 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 10 Oct 2016 09:38:31 +1100 Subject: [PATCH 306/350] Simplified structure. --- src/modes/world_status.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/modes/world_status.cpp b/src/modes/world_status.cpp index b2119c566..6f053e299 100644 --- a/src/modes/world_status.cpp +++ b/src/modes/world_status.cpp @@ -243,13 +243,8 @@ void WorldStatus::updateTime(const float dt) if(NetworkConfig::get()->isServer()) RaceEventManager::getInstance()->startReadySetGo(); m_server_is_ready = true; - m_phase = WAIT_FOR_SERVER_PHASE; } // if not networked - else if(NetworkConfig::get()->isNetworking()) - { - // must be client now - m_phase = WAIT_FOR_SERVER_PHASE; - } + m_phase = WAIT_FOR_SERVER_PHASE; return; // Don't increase time case WAIT_FOR_SERVER_PHASE: // On a client this phase waits for the server to be ready. On a From b2cdc8c97eea372d970d6cb02ba581b761d9fd90 Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 11 Oct 2016 16:25:22 +0800 Subject: [PATCH 307/350] Fix memory leak of unicolor texture Also try to hold them until the last moment, this may need some testing. --- src/graphics/irr_driver.cpp | 2 ++ src/graphics/texture_manager.cpp | 13 +++++++++++-- src/graphics/texture_manager.hpp | 1 + 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 7538f6bcb..4a527d721 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -160,6 +160,7 @@ IrrDriver::~IrrDriver() } assert(m_device != NULL); + cleanUnicolorTextures(); m_device->drop(); m_device = NULL; m_modes.clear(); @@ -905,6 +906,7 @@ void IrrDriver::applyResolutionSettings() { Shaders::destroy(); } + cleanUnicolorTextures(); initDevice(); font_manager = new FontManager(); diff --git a/src/graphics/texture_manager.cpp b/src/graphics/texture_manager.cpp index fce9c61f7..7047446cd 100644 --- a/src/graphics/texture_manager.cpp +++ b/src/graphics/texture_manager.cpp @@ -59,6 +59,14 @@ static std::map unicolor_cache; void resetTextureTable() { AlreadyTransformedTexture.clear(); +} + +void cleanUnicolorTextures() +{ + for (std::pair& uc : unicolor_cache) + { + uc.second->drop(); + } unicolor_cache.clear(); } @@ -228,7 +236,6 @@ video::ITexture* getUnicolorTexture(const video::SColor &c) std::map::iterator it = unicolor_cache.find(c.color); if (it != unicolor_cache.end()) { - it->second->grab(); return it->second; } else @@ -240,10 +247,12 @@ video::ITexture* getUnicolorTexture(const video::SColor &c) c.color }; video::IImage *img = irr_driver->getVideoDriver()->createImageFromData(video::ECF_A8R8G8B8, core::dimension2d(2, 2), tmp); - img->grab(); std::stringstream name; name << "color" << c.color; video::ITexture* tex = irr_driver->getVideoDriver()->addTexture(name.str().c_str(), img); + tex->grab(); + // Only let our map hold the unicolor texture + irr_driver->getVideoDriver()->removeTexture(tex); unicolor_cache[c.color] = tex; img->drop(); return tex; diff --git a/src/graphics/texture_manager.hpp b/src/graphics/texture_manager.hpp index afa82a4e5..d2e2bd0d7 100644 --- a/src/graphics/texture_manager.hpp +++ b/src/graphics/texture_manager.hpp @@ -26,6 +26,7 @@ GLuint getTextureGLuint(irr::video::ITexture *tex); GLuint getDepthTexture(irr::video::ITexture *tex); void resetTextureTable(); +void cleanUnicolorTextures(); void compressTexture(irr::video::ITexture *tex, bool srgb, bool premul_alpha = false); bool loadCompressedTexture(const std::string& compressed_tex); void saveCompressedTexture(const std::string& compressed_tex); From 7df560ae6346c55069c5ad0a0771e329bae0f8de Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 12 Oct 2016 09:30:08 +0800 Subject: [PATCH 308/350] Fix leaking of scripting engine --- src/scriptengine/script_engine.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/scriptengine/script_engine.cpp b/src/scriptengine/script_engine.cpp index f946d131b..5f2d05171 100644 --- a/src/scriptengine/script_engine.cpp +++ b/src/scriptengine/script_engine.cpp @@ -77,6 +77,8 @@ namespace Scripting ScriptEngine::~ScriptEngine() { // Release the engine + m_pending_timeouts.clearAndDeleteAll(); + m_engine->DiscardModule(MODULE_ID_MAIN_SCRIPT_FILE); m_engine->Release(); } From 76f32b4a41e04e6045471f2b11bbb119c2016af4 Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 12 Oct 2016 09:51:44 +0800 Subject: [PATCH 309/350] Fix an unfree Atom --- lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp index a8a208c7d..b84a5cfcc 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp @@ -899,7 +899,7 @@ bool CIrrDeviceLinux::createWindow() WMCheck, 0L, 1L, False, XA_WINDOW, &type, &form, &len, &remain, (unsigned char **)&list); - + XFree(list); bool netWM = (s == Success) && len; attributes.override_redirect = !netWM && CreationParams.Fullscreen; From ddcf6fbb14ccc85c33840ba7e9535805519a8efe Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 12 Oct 2016 10:28:33 +0800 Subject: [PATCH 310/350] Fix memory leaking when changing resolution --- src/graphics/irr_driver.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 4a527d721..310e9237c 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -870,11 +870,11 @@ void IrrDriver::applyResolutionSettings() UserConfigParams::m_prev_height) ); m_video_driver->endScene(); track_manager->removeAllCachedData(); - attachment_manager->removeTextures(); + delete attachment_manager; projectile_manager->removeTextures(); ItemManager::removeTextures(); kart_properties_manager->unloadAllKarts(); - powerup_manager->unloadPowerups(); + delete powerup_manager; Referee::cleanup(); ParticleKindManager::get()->cleanup(); delete input_manager; @@ -901,12 +901,23 @@ void IrrDriver::applyResolutionSettings() RSMPassCmd::getInstance()->kill(); GlowPassCmd::getInstance()->kill(); resetTextureTable(); - // initDevice will drop the current device. + + if (m_post_processing) + { + // check if we createad the OpenGL device by calling initDevice() + m_post_processing->drop(); + } + cleanUnicolorTextures(); + + delete m_shadow_matrices; + if (CVS->isGLSL()) { Shaders::destroy(); } - cleanUnicolorTextures(); + delete m_spherical_harmonics; + + // initDevice will drop the current device. initDevice(); font_manager = new FontManager(); @@ -919,6 +930,8 @@ void IrrDriver::applyResolutionSettings() material_manager->loadMaterial(); input_manager = new InputManager (); input_manager->setMode(InputManager::MENU); + powerup_manager = new PowerupManager(); + attachment_manager = new AttachmentManager(); GUIEngine::addLoadingIcon( irr_driver->getTexture(file_manager From 48e6128e12ae3300447d2ef7e266f62b20fc2dfd Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 12 Oct 2016 11:48:18 +0800 Subject: [PATCH 311/350] Grab light in the correct place, fixed all leaking of lights --- src/graphics/irr_driver.cpp | 6 +----- src/karts/kart_gfx.cpp | 7 +++++++ src/tracks/track.cpp | 4 +++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 310e9237c..c335a7a03 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -2664,8 +2664,6 @@ scene::ISceneNode *IrrDriver::addLight(const core::vector3df &pos, else light = new SunNode(m_scene_manager, parent, r, g, b); - light->grab(); - light->setPosition(pos); light->updateAbsolutePosition(); @@ -2695,9 +2693,7 @@ scene::ISceneNode *IrrDriver::addLight(const core::vector3df &pos, void IrrDriver::clearLights() { - u32 i; - const u32 max = (int)m_lights.size(); - for (i = 0; i < max; i++) + for (unsigned int i = 0; i < m_lights.size(); i++) { m_lights[i]->drop(); } diff --git a/src/karts/kart_gfx.cpp b/src/karts/kart_gfx.cpp index 989495245..829a6749b 100644 --- a/src/karts/kart_gfx.cpp +++ b/src/karts/kart_gfx.cpp @@ -80,6 +80,13 @@ KartGFX::KartGFX(const AbstractKart *kart) m_skidding_light_2->setName( ("skidding emitter 2 (" + m_kart->getIdent() + ")").c_str() ); + if (CVS->isGLSL()) + { + m_nitro_light->grab(); + m_skidding_light_1->grab(); + m_skidding_light_2->grab(); + } + // Create particle effects Vec3 rear_left(kart->getWheelGraphicsPosition(3).getX(), 0.05f, kart->getWheelGraphicsPosition(3).getZ()-0.1f ); diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index a24d79a6b..e01eaffed 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -1868,8 +1868,10 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id) sun->getLightData().SpecularColor = m_sun_specular_color; } else + { irr_driver->createSunInterposer(); - + m_sun->grab(); + } createPhysicsModel(main_track_count); From 965e901796b6f7c6923068b67a125518c10d89bf Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 12 Oct 2016 14:00:51 +0800 Subject: [PATCH 312/350] Fix typo --- src/modes/three_strikes_battle.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modes/three_strikes_battle.cpp b/src/modes/three_strikes_battle.cpp index f8bf91434..f83375dad 100644 --- a/src/modes/three_strikes_battle.cpp +++ b/src/modes/three_strikes_battle.cpp @@ -74,7 +74,7 @@ ThreeStrikesBattle::~ThreeStrikesBattle() { m_tires.clearWithoutDeleting(); - irr_driver->grabAllTextures(m_tire); + irr_driver->dropAllTextures(m_tire); // Remove the mesh from the cache so that the mesh is properly // freed once all refernces to it (which will happen once all // karts are being freed, which would have a pointer to this mesh) From 061827a1ac9994194484f4c28ef8ef7273bcf78e Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 12 Oct 2016 17:09:57 +1100 Subject: [PATCH 313/350] Ported to use the new server (Issue65 branch). --- src/network/protocols/server_lobby_room_protocol.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/network/protocols/server_lobby_room_protocol.cpp b/src/network/protocols/server_lobby_room_protocol.cpp index d3dbeee8e..a525f63e1 100644 --- a/src/network/protocols/server_lobby_room_protocol.cpp +++ b/src/network/protocols/server_lobby_room_protocol.cpp @@ -206,11 +206,7 @@ void ServerLobbyRoomProtocol::registerServer() { Online::XMLRequest *request = new Online::XMLRequest(); const TransportAddress& addr = NetworkConfig::get()->getMyAddress(); -#ifdef NEW_PROTOCOL - PlayerManager::setUserDetails(request, "register", Online::API::SERVER_PATH); -#else - PlayerManager::setUserDetails(request, "start", Online::API::SERVER_PATH); -#endif + PlayerManager::setUserDetails(request, "create", Online::API::SERVER_PATH); request->addParameter("address", addr.getIP() ); request->addParameter("port", addr.getPort() ); request->addParameter("private_port", From 83ffbd96e13bfe716ff6d46716353b0fd7a3a7f0 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 12 Oct 2016 17:21:15 +1100 Subject: [PATCH 314/350] Better error message in case of a failure to creaet a server. --- src/network/protocols/server_lobby_room_protocol.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/protocols/server_lobby_room_protocol.cpp b/src/network/protocols/server_lobby_room_protocol.cpp index a525f63e1..cd8462041 100644 --- a/src/network/protocols/server_lobby_room_protocol.cpp +++ b/src/network/protocols/server_lobby_room_protocol.cpp @@ -230,7 +230,7 @@ void ServerLobbyRoomProtocol::registerServer() { irr::core::stringc error(request->getInfo().c_str()); Log::error("RegisterServer", "%s", error.c_str()); - STKHost::get()->setErrorMessage(_("Failed to register server")); + STKHost::get()->setErrorMessage(_("Failed to register server: %s", error.c_str())); } } // registerServer From e09fe6ff8a5458161c0cea8e184fead84d8f7185 Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 13 Oct 2016 14:08:58 +0800 Subject: [PATCH 315/350] Fix memory leak of mesh tool --- src/graphics/mesh_tools.cpp | 38 ++++++++++++++++++------------------- src/tracks/track.cpp | 2 +- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/graphics/mesh_tools.cpp b/src/graphics/mesh_tools.cpp index f9698acc6..b3792b1d5 100644 --- a/src/graphics/mesh_tools.cpp +++ b/src/graphics/mesh_tools.cpp @@ -444,29 +444,27 @@ scene::IMesh* MeshTools::createMeshWithTangents(scene::IMesh* mesh, clone->recalculateBoundingBox(); if (calculate_tangents) recalculateTangents(clone, recalculate_normals, smooth, angle_weighted); - - int mbcount = clone->getMeshBufferCount(); - for (int i = 0; i < mbcount; i++) - { - scene::IMeshBuffer* mb = clone->getMeshBuffer(i); - - for (u32 t = 0; t < video::MATERIAL_MAX_TEXTURES; t++) - { - video::ITexture* texture = mb->getMaterial().TextureLayer[t].Texture; - if (texture != NULL) - texture->grab(); - } - } scene::IMeshCache* meshCache = irr_driver->getSceneManager()->getMeshCache(); io::SNamedPath path = meshCache->getMeshName(mesh); - irr_driver->removeMeshFromCache(mesh); + if (path.getPath() == "") + { + // This mesh is not in irrlicht cache, drop it directly + assert(mesh->getReferenceCount() == 1); + mesh->drop(); + return clone; + } + else + { + // Cache the calcuated tangent mesh with path + irr_driver->removeMeshFromCache(mesh); + scene::SAnimatedMesh* amesh = new scene::SAnimatedMesh(clone); + clone->drop(); + irr_driver->grabAllTextures(amesh); + meshCache->addMesh(path, amesh); + World::getWorld()->getTrack()->addCachedMesh(amesh); - scene::SAnimatedMesh* amesh = new scene::SAnimatedMesh(clone); - meshCache->addMesh(path, amesh); + return amesh; + } - World::getWorld()->getTrack()->addCachedMesh(amesh); - - return clone; } - diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index e01eaffed..39ce3d986 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -1174,7 +1174,7 @@ bool Track::loadMainTrack(const XMLNode &root) handleAnimatedTextures(scene_node, *track_node); m_all_nodes.push_back(scene_node); - MeshTools::minMax3D(merged_mesh, &m_aabb_min, &m_aabb_max); + MeshTools::minMax3D(tangent_mesh, &m_aabb_min, &m_aabb_max); // Increase the maximum height of the track: since items that fly // too high explode, e.g. cakes can not be show when being at the // top of the track (since they will explode when leaving the AABB From f70a64f61a043effd236f69fd71078634930ce05 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 14 Oct 2016 12:01:37 +0800 Subject: [PATCH 316/350] Fix controller and text billboard leaking in kart --- src/karts/kart.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 05bbe88ab..5efa6f4c6 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -332,6 +332,7 @@ void Kart::reset() // restore the original controller. if(m_saved_controller) { + delete m_controller; m_controller = m_saved_controller; m_saved_controller = NULL; } @@ -2888,12 +2889,14 @@ void Kart::setOnScreenText(const wchar_t *text) if (CVS->isGLSL()) { - new STKTextBillboard(text, bold_face, + scene::ISceneNode* tb = + new STKTextBillboard(text, bold_face, GUIEngine::getSkin()->getColor("font::bottom"), GUIEngine::getSkin()->getColor("font::top"), getNode(), irr_driver->getSceneManager(), -1, core::vector3df(0.0f, 1.5f, 0.0f), core::vector3df(1.0f, 1.0f, 1.0f)); + tb->drop(); } else { From 35e382b893d0bbcfadb24236f4e64608ec45f9fe Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 14 Oct 2016 17:48:22 +1100 Subject: [PATCH 317/350] Remove unnecessary request for server address,since it is now included for each server in the list of all servers. --- src/network/protocols/connect_to_server.cpp | 12 ------------ src/network/server.cpp | 8 +++++++- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/network/protocols/connect_to_server.cpp b/src/network/protocols/connect_to_server.cpp index d21a00888..9ef2acb0d 100644 --- a/src/network/protocols/connect_to_server.cpp +++ b/src/network/protocols/connect_to_server.cpp @@ -132,14 +132,7 @@ void ConnectToServer::asynchronousUpdate() } else { - // No quick connect, so we have a server to connect to. - // Find its address - m_current_protocol = new GetPeerAddress(m_host_id, this); - m_current_protocol->requestStart(); m_state = GOT_SERVER_ADDRESS; - // Pause this protocol till GetPeerAddress finishes. - // The callback then will unpause this protocol/ - requestPause(); } } break; @@ -270,11 +263,6 @@ void ConnectToServer::callback(Protocol *protocol) // STKHost, so we only need to unpause this protocol requestUnpause(); break; - case GOT_SERVER_ADDRESS: - // Get the server address from the protocol. - m_server_address.copy(((GetPeerAddress*)protocol)->getAddress()); - requestUnpause(); - break; default: Log::error("ConnectToServer", "Received unexpected callback while in state %d.", diff --git a/src/network/server.cpp b/src/network/server.cpp index ffce41b81..a0754ddd1 100644 --- a/src/network/server.cpp +++ b/src/network/server.cpp @@ -49,7 +49,13 @@ Server::Server(const XMLNode & xml, bool is_lan) xml.get("hostid", &m_host_id); xml.get("max_players", &m_max_players); xml.get("current_players", &m_current_players); - + uint32_t ip; + xml.get("ip", &ip); + m_address.setIP(ip); + uint16_t port; + xml.get("port", &port); + m_address.setPort(port); + xml.get("private_port", &m_private_port); } // Server(const XML&) // ---------------------------------------------------------------------------- From efb6b7431a0fa1e8c20ea3e1dc3de664921737e5 Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 15 Oct 2016 00:17:09 +0800 Subject: [PATCH 318/350] Try to make skinned mesh work with normal map --- src/graphics/mesh_tools.cpp | 66 ++++++++++++++++++++++++++++-- src/graphics/mesh_tools.hpp | 12 ++++-- src/graphics/stk_animated_mesh.cpp | 2 +- src/karts/kart_model.cpp | 17 ++++++++ 4 files changed, 90 insertions(+), 7 deletions(-) diff --git a/src/graphics/mesh_tools.cpp b/src/graphics/mesh_tools.cpp index b3792b1d5..95bf286ee 100644 --- a/src/graphics/mesh_tools.cpp +++ b/src/graphics/mesh_tools.cpp @@ -28,6 +28,7 @@ #include #include #include +#include void MeshTools::minMax3D(scene::IMesh* mesh, Vec3 *min, Vec3 *max) { @@ -460,11 +461,70 @@ scene::IMesh* MeshTools::createMeshWithTangents(scene::IMesh* mesh, irr_driver->removeMeshFromCache(mesh); scene::SAnimatedMesh* amesh = new scene::SAnimatedMesh(clone); clone->drop(); - irr_driver->grabAllTextures(amesh); meshCache->addMesh(path, amesh); - World::getWorld()->getTrack()->addCachedMesh(amesh); - + if (World::getWorld()) + { + irr_driver->grabAllTextures(amesh); + World::getWorld()->getTrack()->addCachedMesh(amesh); + return amesh; + } + amesh->drop(); return amesh; } } + +void MeshTools::createSkinnedMeshWithTangents(scene::ISkinnedMesh* mesh, + bool(*predicate)(scene::IMeshBuffer*)) +{ + core::array& all_mb = mesh->getMeshBuffers(); + const int all_mb_size = all_mb.size(); + for (int i = 0; i < all_mb_size; i++) + { + scene::SSkinMeshBuffer* mb = all_mb[i]; + if (mb && predicate(mb)) + { + mb->convertToTangents(); + const int index_count = mb->getIndexCount(); + uint16_t* idx = mb->getIndices(); + video::S3DVertexTangents* v = + (video::S3DVertexTangents*)mb->getVertices(); + + for (int i = 0; i < index_count; i += 3) + { + calculateTangents( + v[idx[i+0]].Normal, + v[idx[i+0]].Tangent, + v[idx[i+0]].Binormal, + v[idx[i+0]].Pos, + v[idx[i+1]].Pos, + v[idx[i+2]].Pos, + v[idx[i+0]].TCoords, + v[idx[i+1]].TCoords, + v[idx[i+2]].TCoords); + + calculateTangents( + v[idx[i+1]].Normal, + v[idx[i+1]].Tangent, + v[idx[i+1]].Binormal, + v[idx[i+1]].Pos, + v[idx[i+2]].Pos, + v[idx[i+0]].Pos, + v[idx[i+1]].TCoords, + v[idx[i+2]].TCoords, + v[idx[i+0]].TCoords); + + calculateTangents( + v[idx[i+2]].Normal, + v[idx[i+2]].Tangent, + v[idx[i+2]].Binormal, + v[idx[i+2]].Pos, + v[idx[i+0]].Pos, + v[idx[i+1]].Pos, + v[idx[i+2]].TCoords, + v[idx[i+0]].TCoords, + v[idx[i+1]].TCoords); + } + } + } +} diff --git a/src/graphics/mesh_tools.hpp b/src/graphics/mesh_tools.hpp index e1e28076e..8529ef137 100644 --- a/src/graphics/mesh_tools.hpp +++ b/src/graphics/mesh_tools.hpp @@ -21,7 +21,7 @@ namespace irr { - namespace scene { class IMesh; class IMeshBuffer; } + namespace scene { class IMesh; class IMeshBuffer; class ISkinnedMesh; } } using namespace irr; @@ -37,8 +37,14 @@ namespace MeshTools bool isNormalMap(scene::IMeshBuffer* mb); // Copied from irrlicht - scene::IMesh* createMeshWithTangents(scene::IMesh* mesh, bool(*predicate)(scene::IMeshBuffer*), - bool recalculateNormals = false, bool smooth = false, bool angleWeighted = false, bool calculateTangents = true); + scene::IMesh* createMeshWithTangents(scene::IMesh* mesh, + bool(*predicate)(scene::IMeshBuffer*), bool recalculateNormals = false, + bool smooth = false, bool angleWeighted = false, + bool calculateTangents = true); + + void createSkinnedMeshWithTangents(scene::ISkinnedMesh* mesh, + bool(*predicate)(scene::IMeshBuffer*)); + } // MeshTools #endif diff --git a/src/graphics/stk_animated_mesh.cpp b/src/graphics/stk_animated_mesh.cpp index 949f4e7e4..0a653b561 100644 --- a/src/graphics/stk_animated_mesh.cpp +++ b/src/graphics/stk_animated_mesh.cpp @@ -169,7 +169,7 @@ void STKAnimatedMesh::updateNoGL() } else { - Material::ShaderType MatType = material->getShaderType();// getMeshMaterialFromType(type, mb->getVertexType(), material); + Material::ShaderType MatType = getMeshMaterialFromType(type, mb->getVertexType(), material, NULL); MeshSolidMaterial[MatType].push_back(&mesh); } } diff --git a/src/karts/kart_model.cpp b/src/karts/kart_model.cpp index 9bc8ad12c..9517d344d 100644 --- a/src/karts/kart_model.cpp +++ b/src/karts/kart_model.cpp @@ -522,6 +522,13 @@ bool KartModel::loadModels(const KartProperties &kart_properties) full_path.c_str(), kart_properties.getIdent().c_str()); return false; } + + scene::ISkinnedMesh* sm = dynamic_cast(m_mesh); + if (sm) + { + MeshTools::createSkinnedMeshWithTangents(sm, &MeshTools::isNormalMap); + } + m_mesh->grab(); irr_driver->grabAllTextures(m_mesh); @@ -570,6 +577,14 @@ bool KartModel::loadModels(const KartProperties &kart_properties) // Grab all textures. This is done for the master only, so // the destructor will only free the textures if a master // copy is freed. + + scene::ISkinnedMesh* sm = + dynamic_cast(obj.m_model); + if (sm) + { + MeshTools::createSkinnedMeshWithTangents(sm, + &MeshTools::isNormalMap); + } irr_driver->grabAllTextures(obj.m_model); // Update min/max @@ -612,6 +627,8 @@ bool KartModel::loadModels(const KartProperties &kart_properties) std::string full_wheel = kart_properties.getKartDir()+m_wheel_filename[i]; m_wheel_model[i] = irr_driver->getMesh(full_wheel); + m_wheel_model[i] = MeshTools::createMeshWithTangents(m_wheel_model[i], + &MeshTools::isNormalMap); // Grab all textures. This is done for the master only, so // the destructor will only free the textures if a master // copy is freed. From 604e454f33ff97c016666e204203873f5443f666 Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 15 Oct 2016 15:21:34 +0800 Subject: [PATCH 319/350] Fix normal map in library and track objects if they are animated --- src/tracks/model_definition_loader.cpp | 8 +++++++- src/tracks/track_object_presentation.cpp | 10 ++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/tracks/model_definition_loader.cpp b/src/tracks/model_definition_loader.cpp index 5863b484e..e487aab53 100644 --- a/src/tracks/model_definition_loader.cpp +++ b/src/tracks/model_definition_loader.cpp @@ -82,7 +82,13 @@ LODNode* ModelDefinitionLoader::instanciateAsLOD(const XMLNode* node, scene::ISc continue; } - irr_driver->setAllMaterialFlags(a_mesh); + scene::ISkinnedMesh* sm = + dynamic_cast(a_mesh); + if (sm) + { + MeshTools::createSkinnedMeshWithTangents(sm, + &MeshTools::isNormalMap); + } a_mesh->grab(); //cache.push_back(a_mesh); diff --git a/src/tracks/track_object_presentation.cpp b/src/tracks/track_object_presentation.cpp index 4472d5885..3c98ca224 100644 --- a/src/tracks/track_object_presentation.cpp +++ b/src/tracks/track_object_presentation.cpp @@ -368,6 +368,16 @@ TrackObjectPresentationMesh::TrackObjectPresentationMesh( m_mesh = MeshTools::createMeshWithTangents(m_mesh, &MeshTools::isNormalMap); } + else + { + scene::ISkinnedMesh* sm = + dynamic_cast(m_mesh); + if (sm) + { + MeshTools::createSkinnedMeshWithTangents(sm, + &MeshTools::isNormalMap); + } + } init(&xml_node, parent, enabled); } // TrackObjectPresentationMesh From 020edf16790c61c77f2eefc32b15da51827f20d8 Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Sat, 15 Oct 2016 18:56:13 -0400 Subject: [PATCH 320/350] Add support for directional (forward-only or backwards-only) driveline segments --- src/modes/linear_world.cpp | 12 +++++++++- src/tracks/arena_graph.cpp | 3 ++- src/tracks/drive_graph.cpp | 31 ++++++++++++++++++++------ src/tracks/drive_node.cpp | 4 ++-- src/tracks/drive_node.hpp | 3 ++- src/tracks/drive_node_2d.cpp | 4 ++-- src/tracks/drive_node_2d.hpp | 2 +- src/tracks/drive_node_3d.cpp | 4 ++-- src/tracks/drive_node_3d.hpp | 2 +- src/tracks/graph.cpp | 43 +++++++++++++++++++----------------- src/tracks/graph.hpp | 2 +- src/tracks/quad.cpp | 17 +++++++------- src/tracks/quad.hpp | 6 ++++- 13 files changed, 85 insertions(+), 48 deletions(-) diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index 8432ccc2f..4b13ce270 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -620,7 +620,17 @@ unsigned int LinearWorld::getRescuePositionIndex(AbstractKart *kart) // Setting XYZ for the kart is important since otherwise the kart // will not detect the right material again when doing the next // raycast to detect where it is driving on (--> potential rescue loop) - return getTrackSector(kart_id)->getCurrentGraphNode(); + int index = getTrackSector(kart_id)->getCurrentGraphNode(); + + // Do not rescue to an ignored quad, find another (non-ignored) quad + if (Graph::get()->getQuad(index)->isIgnored()) + { + Vec3 pos = kart->getFrontXYZ(); + int sector = Graph::get()->findOutOfRoadSector(pos); + return sector; + } + + return index; } // getRescuePositionIndex // ------------------------------------------------------------------------ diff --git a/src/tracks/arena_graph.cpp b/src/tracks/arena_graph.cpp index 042d4d211..53f3abd4a 100644 --- a/src/tracks/arena_graph.cpp +++ b/src/tracks/arena_graph.cpp @@ -148,7 +148,8 @@ void ArenaGraph::loadNavmesh(const std::string &navmesh) createQuad(all_vertices[quad_index[0]], all_vertices[quad_index[1]], all_vertices[quad_index[2]], all_vertices[quad_index[3]], m_all_nodes.size(), - false/*invisible*/, false/*ai_ignore*/, true/*is_arena*/); + false/*invisible*/, false/*ai_ignore*/, true/*is_arena*/, + false/*ignore*/); ArenaNode* cur_node = getNode(m_all_nodes.size() - 1); cur_node->setAdjacentNodes(adjacent_quad_index); diff --git a/src/tracks/drive_graph.cpp b/src/tracks/drive_graph.cpp index 5f98b1ad8..2ee4259e4 100644 --- a/src/tracks/drive_graph.cpp +++ b/src/tracks/drive_graph.cpp @@ -98,13 +98,13 @@ void DriveGraph::load(const std::string &quad_file_name, } // Each quad is part of the graph exactly once now. - for(unsigned int i=0; igetNumNodes(); i++) + for (unsigned int i = 0; i < quad->getNumNodes(); i++) { const XMLNode *xml_node = quad->getNode(i); - if(xml_node->getName()!="quad") + if (xml_node->getName() != "quad") { Log::warn("DriveGraph: Unsupported node type '%s' found in '%s' - ignored.", - xml_node->getName().c_str(), filename.c_str()); + xml_node->getName().c_str(), filename.c_str()); continue; } @@ -117,12 +117,29 @@ void DriveGraph::load(const std::string &quad_file_name, getPoint(xml_node, "p1", &p1); getPoint(xml_node, "p2", &p2); getPoint(xml_node, "p3", &p3); - bool invisible=false; + bool invisible = false; xml_node->get("invisible", &invisible); - bool ai_ignore=false; + bool ai_ignore = false; xml_node->get("ai-ignore", &ai_ignore); + + bool ignored = false; + std::string direction; + xml_node->get("direction", &direction); + if (direction == "forward" && race_manager->getReverseTrack()) + { + ignored = true; + invisible = true; + ai_ignore = true; + } + else if (direction == "reverse" && !race_manager->getReverseTrack()) + { + ignored = true; + invisible = true; + ai_ignore = true; + } + createQuad(p0, p1, p2, p3, m_all_nodes.size(), invisible, ai_ignore, - false/*is_arena*/); + false/*is_arena*/, ignored); } delete quad; @@ -156,7 +173,7 @@ void DriveGraph::load(const std::string &quad_file_name, const XMLNode *xml_node = xml->getNode(node_index); // Load the definition of edges between the graph nodes: // ----------------------------------------------------- - if(xml_node->getName()=="node-list") + if (xml_node->getName() == "node-list") { // Each quad is part of the graph exactly once now. unsigned int to = 0; diff --git a/src/tracks/drive_node.cpp b/src/tracks/drive_node.cpp index eed79e85f..c59d066b0 100644 --- a/src/tracks/drive_node.cpp +++ b/src/tracks/drive_node.cpp @@ -28,8 +28,8 @@ DriveNode::DriveNode(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, const Vec3 &normal, unsigned int node_index, bool invisible, - bool ai_ignore) - :Quad(p0, p1, p2, p3, normal, node_index, invisible) + bool ai_ignore, bool ignored) + :Quad(p0, p1, p2, p3, normal, node_index, invisible, ignored) { m_ai_ignore = ai_ignore; m_distance_from_start = -1.0f; diff --git a/src/tracks/drive_node.hpp b/src/tracks/drive_node.hpp index 3497f64b3..b56c23800 100644 --- a/src/tracks/drive_node.hpp +++ b/src/tracks/drive_node.hpp @@ -96,6 +96,7 @@ private: */ std::vector< int > m_checkline_requirements; + // ------------------------------------------------------------------------ void markAllSuccessorsToUse(unsigned int n, PathToNodeVector *m_path_to_node); @@ -104,7 +105,7 @@ public: DriveNode(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, const Vec3 &normal, unsigned int node_index, bool invisible, - bool ai_ignore); + bool ai_ignore, bool ignored); // ------------------------------------------------------------------------ virtual ~DriveNode() {} // ------------------------------------------------------------------------ diff --git a/src/tracks/drive_node_2d.cpp b/src/tracks/drive_node_2d.cpp index 058b6f159..b08e44694 100644 --- a/src/tracks/drive_node_2d.cpp +++ b/src/tracks/drive_node_2d.cpp @@ -22,9 +22,9 @@ DriveNode2D::DriveNode2D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, const Vec3 &normal, unsigned int node_index, bool invisible, - bool ai_ignore) + bool ai_ignore, bool ignored) : DriveNode(p0, p1, p2, p3, normal, node_index, invisible, - ai_ignore) + ai_ignore, ignored) { m_line = core::line2df(m_upper_center.getX(), m_upper_center.getZ(), m_lower_center.getX(), m_lower_center.getZ()); diff --git a/src/tracks/drive_node_2d.hpp b/src/tracks/drive_node_2d.hpp index c5a7b6f3e..bca81694b 100644 --- a/src/tracks/drive_node_2d.hpp +++ b/src/tracks/drive_node_2d.hpp @@ -45,7 +45,7 @@ private: public: DriveNode2D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, const Vec3 &normal, unsigned int node_index, bool invisible, - bool ai_ignore); + bool ai_ignore, bool ignored); // ------------------------------------------------------------------------ virtual void getDistances(const Vec3 &xyz, Vec3 *result) const OVERRIDE; // ------------------------------------------------------------------------ diff --git a/src/tracks/drive_node_3d.cpp b/src/tracks/drive_node_3d.cpp index 133d2c159..6a7a92b55 100644 --- a/src/tracks/drive_node_3d.cpp +++ b/src/tracks/drive_node_3d.cpp @@ -22,9 +22,9 @@ DriveNode3D::DriveNode3D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, const Vec3 &normal, unsigned int node_index, bool invisible, - bool ai_ignore) + bool ai_ignore, bool ignored) : DriveNode(p0, p1, p2, p3, normal, node_index, invisible, - ai_ignore), BoundingBox3D(p0, p1, p2, p3, normal) + ai_ignore, ignored), BoundingBox3D(p0, p1, p2, p3, normal) { m_line = core::line3df(m_lower_center.toIrrVector(), m_upper_center.toIrrVector()); diff --git a/src/tracks/drive_node_3d.hpp b/src/tracks/drive_node_3d.hpp index 5f763c3a6..162498a10 100644 --- a/src/tracks/drive_node_3d.hpp +++ b/src/tracks/drive_node_3d.hpp @@ -38,7 +38,7 @@ private: public: DriveNode3D(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, const Vec3 &normal, unsigned int node_index, bool invisible, - bool ai_ignore); + bool ai_ignore, bool ignored); // ------------------------------------------------------------------------ virtual bool pointInside(const Vec3& p, bool ignore_vertical = false) const OVERRIDE diff --git a/src/tracks/graph.cpp b/src/tracks/graph.cpp index ff9dc9bea..7fd7610e8 100644 --- a/src/tracks/graph.cpp +++ b/src/tracks/graph.cpp @@ -399,7 +399,7 @@ void Graph::mapPoint2MiniMap(const Vec3 &xyz,Vec3 *draw_at) const // ----------------------------------------------------------------------------- void Graph::createQuad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, unsigned int node_index, - bool invisible, bool ai_ignore, bool is_arena) + bool invisible, bool ai_ignore, bool is_arena, bool ignored) { // Find the normal of this quad by computing the normal of two triangles // and taking their average. @@ -427,7 +427,7 @@ void Graph::createQuad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, else { q = new DriveNode3D(p0, p1, p2, p3, normal, node_index, invisible, - ai_ignore); + ai_ignore, ignored); } } else @@ -441,7 +441,7 @@ void Graph::createQuad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, else { q = new DriveNode2D(p0, p1, p2, p3, normal, node_index, invisible, - ai_ignore); + ai_ignore, ignored); } } m_all_nodes.push_back(q); @@ -499,7 +499,7 @@ void Graph::findRoadSector(const Vec3& xyz, int *sector, else indx = indx<(int)m_all_nodes.size()-1 ? indx +1 : 0; const Quad* q = getQuad(indx); - if(q->pointInside(xyz, ignore_vertical)) + if (q->pointInside(xyz, ignore_vertical)) { *sector = indx; return; @@ -585,24 +585,27 @@ int Graph::findOutOfRoadSector(const Vec3& xyz, const int curr_sector, ? 0 : current_sector+1; - // A first simple test uses the 2d distance to the center of the - // quad. - float dist_2 = - m_all_nodes[next_sector]->getDistance2FromPoint(xyz); - if(dist_2isIgnored()) { - const Quad* q = getQuad(next_sector); - float dist = xyz.getY() - q->getMinHeight(); - // While negative distances are unlikely, we allow some small - // negative numbers in case that the kart is partly in the - // track. Only do the height test in phase==0, in phase==1 - // accept any point, independent of height, or this node is 3d - // which already takes height into account - if(phase==1 || (dist < 5.0f && dist>-1.0f) || - q->is3DQuad() || ignore_vertical) + // A first simple test uses the 2d distance to the center of the + // quad. + float dist_2 = + m_all_nodes[next_sector]->getDistance2FromPoint(xyz); + if (dist_2 < min_dist_2) { - min_dist_2 = dist_2; - min_sector = next_sector; + float dist = xyz.getY() - q->getMinHeight(); + // While negative distances are unlikely, we allow some small + // negative numbers in case that the kart is partly in the + // track. Only do the height test in phase==0, in phase==1 + // accept any point, independent of height, or this node is 3d + // which already takes height into account + if (phase == 1 || (dist < 5.0f && dist>-1.0f) || + q->is3DQuad() || ignore_vertical) + { + min_dist_2 = dist_2; + min_sector = next_sector; + } } } current_sector = next_sector; diff --git a/src/tracks/graph.hpp b/src/tracks/graph.hpp index 8f713cb6e..d132be82b 100644 --- a/src/tracks/graph.hpp +++ b/src/tracks/graph.hpp @@ -61,7 +61,7 @@ protected: * graph. */ void createQuad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, unsigned int node_index, - bool invisible, bool ai_ignore, bool is_arena); + bool invisible, bool ai_ignore, bool is_arena, bool ignore); private: /** The 2d bounding box, used for hashing. */ diff --git a/src/tracks/quad.cpp b/src/tracks/quad.cpp index 2b3bf3922..93e143ab1 100644 --- a/src/tracks/quad.cpp +++ b/src/tracks/quad.cpp @@ -25,16 +25,17 @@ /** Constructor, takes 4 points. */ Quad::Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, - const Vec3 &normal, int index, bool invisible) + const Vec3 &normal, int index, bool invisible, bool ignored) : m_index(index), m_normal(normal), m_invisible(invisible) { - m_p[0]=p0; m_p[1]=p1; m_p[2]=p2; m_p[3]=p3; - - m_center = 0.25f*(p0+p1+p2+p3); - m_min_height = std::min ( std::min(p0.getY(), p1.getY()), - std::min(p2.getY(), p3.getY()) ); - m_max_height = std::max ( std::max(p0.getY(), p1.getY()), - std::max(p2.getY(), p3.getY()) ); + m_is_ignored = ignored; + m_p[0]=p0; m_p[1]=p1; m_p[2]=p2; m_p[3]=p3; + + m_center = 0.25f*(p0+p1+p2+p3); + m_min_height = std::min ( std::min(p0.getY(), p1.getY()), + std::min(p2.getY(), p3.getY()) ); + m_max_height = std::max ( std::max(p0.getY(), p1.getY()), + std::max(p2.getY(), p3.getY()) ); } // Quad // ---------------------------------------------------------------------------- diff --git a/src/tracks/quad.hpp b/src/tracks/quad.hpp index 212396cc5..51f26cbc4 100644 --- a/src/tracks/quad.hpp +++ b/src/tracks/quad.hpp @@ -54,6 +54,8 @@ private: /** Set to true if this quad should not be shown in the minimap. */ bool m_invisible; + bool m_is_ignored; + /** The minimum height of the quad, used in case that several quads * are on top of each other when determining the sector a kart is on. */ float m_min_height; @@ -67,7 +69,7 @@ public: // ------------------------------------------------------------------------ Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, const Vec3 & normal = Vec3(0, 1, 0), int index = -1, - bool invisible = false); + bool invisible = false, bool ignored = false); // ------------------------------------------------------------------------ virtual ~Quad() {} // ------------------------------------------------------------------------ @@ -93,6 +95,8 @@ public: /** Returns true of this quad is invisible, i.e. not to be shown in * the minimap. */ bool isInvisible() const { return m_invisible; } + // ------------------------------------------------------------------------ + bool isIgnored() const { return m_is_ignored; } // ------------------------------------------------------------------------ /** Returns the normal of this quad. */ const Vec3& getNormal() const { return m_normal; } From 929b9b5c20152ef0db67a4ec1070a01f3275e1df Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Sat, 15 Oct 2016 20:28:48 -0400 Subject: [PATCH 321/350] Stop world imer when game is paused --- src/modes/world_status.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/modes/world_status.cpp b/src/modes/world_status.cpp index 6f053e299..6cf5cd65d 100644 --- a/src/modes/world_status.cpp +++ b/src/modes/world_status.cpp @@ -379,11 +379,16 @@ void WorldStatus::updateTime(const float dt) default: break; } + IrrlichtDevice *device = irr_driver->getDevice(); + switch (m_clock_mode) { case CLOCK_CHRONO: - m_time += dt; - m_count_up_timer += dt; + if (!device->getTimer()->isStopped()) + { + m_time += dt; + m_count_up_timer += dt; + } break; case CLOCK_COUNTDOWN: // stop countdown when race is over @@ -394,8 +399,11 @@ void WorldStatus::updateTime(const float dt) break; } - m_time -= dt; - m_count_up_timer += dt; + if (!device->getTimer()->isStopped()) + { + m_time -= dt; + m_count_up_timer += dt; + } if(m_time <= 0.0) { From 23a1b08efe1a8e29e8874da2f1db4b904b7f860c Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 16 Oct 2016 15:40:40 +0800 Subject: [PATCH 322/350] Allow normal map karts to be used in ghost race Also split ghost karts out of additive shader, so fixed issues like "drivers are facing backwards", "water in abyss affects ghost karts" Todo: maybe transparency depend on current track color? --- data/shaders/ghost_karts.frag | 19 +++++++++++++++++++ src/graphics/render_geometry.cpp | 20 +++++++++++++++----- src/graphics/shaders.cpp | 9 +++++++++ src/graphics/shaders.hpp | 8 ++++++++ src/graphics/stk_animated_mesh.cpp | 5 ++++- src/graphics/stk_mesh.hpp | 14 ++++++++++++++ src/graphics/stk_mesh_scene_node.cpp | 7 ++++--- src/graphics/stk_scene_manager.cpp | 6 ++++++ 8 files changed, 79 insertions(+), 9 deletions(-) create mode 100644 data/shaders/ghost_karts.frag diff --git a/data/shaders/ghost_karts.frag b/data/shaders/ghost_karts.frag new file mode 100644 index 000000000..18b707682 --- /dev/null +++ b/data/shaders/ghost_karts.frag @@ -0,0 +1,19 @@ +#ifdef Use_Bindless_Texture +layout(bindless_sampler) uniform sampler2D tex; +#else +uniform sampler2D tex; +#endif + +in vec2 uv; +in vec4 color; +out vec4 FragColor; + +void main() +{ + vec4 Color = texture(tex, uv); +#ifdef Use_Bindless_Texture + Color.xyz = pow(Color.xyz, vec3(2.2)); +#endif + Color.xyz *= pow(color.xyz, vec3(2.2)); + FragColor = vec4(Color.rgb * 0.5, 0.5); +} diff --git a/src/graphics/render_geometry.cpp b/src/graphics/render_geometry.cpp index 9b642b936..b8c8da5d5 100644 --- a/src/graphics/render_geometry.cpp +++ b/src/graphics/render_geometry.cpp @@ -1555,14 +1555,24 @@ static video::ITexture *displaceTex = 0; // ---------------------------------------------------------------------------- void IrrDriver::renderTransparent() { - glEnable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - glEnable(GL_BLEND); - glBlendEquation(GL_FUNC_ADD); - glDisable(GL_CULL_FACE); irr_driver->setPhase(TRANSPARENT_PASS); + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + glEnable(GL_BLEND); + glBlendEquation(GL_FUNC_ADD); + glEnable(GL_CULL_FACE); + renderTransparenPass( + TexUnits(RenderGeometry::TexUnit(0, true)), + ListGhostKart::getInstance()); + + renderTransparenPass( + TexUnits(RenderGeometry::TexUnit(0, true)), + ListGhostKartTangents::getInstance()); + + glDepthMask(GL_FALSE); + glDisable(GL_CULL_FACE); for (unsigned i = 0; i < ImmediateDrawList::getInstance()->size(); i++) ImmediateDrawList::getInstance()->at(i)->render(); diff --git a/src/graphics/shaders.cpp b/src/graphics/shaders.cpp index 9a2f69ae4..9b5fca4ba 100644 --- a/src/graphics/shaders.cpp +++ b/src/graphics/shaders.cpp @@ -369,6 +369,15 @@ Shaders::TransparentShader::TransparentShader() assignSamplerNames(0, "tex", ST_TRILINEAR_ANISOTROPIC_FILTERED); } // TransparentShader +// ============================================================================ +Shaders::GhostKartsShader::GhostKartsShader() +{ + loadProgram(OBJECT, GL_VERTEX_SHADER, "object_pass.vert", + GL_FRAGMENT_SHADER, "ghost_karts.frag"); + assignUniforms("ModelMatrix", "TextureMatrix"); + assignSamplerNames(0, "tex", ST_TRILINEAR_ANISOTROPIC_FILTERED); +} // GhostKartsShader + // ============================================================================ Shaders::TransparentFogShader::TransparentFogShader() { diff --git a/src/graphics/shaders.hpp b/src/graphics/shaders.hpp index 19699a0df..44e629c55 100644 --- a/src/graphics/shaders.hpp +++ b/src/graphics/shaders.hpp @@ -126,6 +126,14 @@ public: TransparentShader(); }; // TransparentShader + // ======================================================================== + class GhostKartsShader : public TextureShader + { + public: + GhostKartsShader(); + }; // TransparentShader + // ======================================================================== class TransparentFogShader : public TextureShaderisTransparent()) { - TransparentMesh[TM_ADDITIVE].push_back(&mesh); + if (mesh.VAOType == video::EVT_TANGENTS) + TransparentMesh[TM_GHOST_KART_TANGENTS].push_back(&mesh); + else + TransparentMesh[TM_GHOST_KART].push_back(&mesh); } else { diff --git a/src/graphics/stk_mesh.hpp b/src/graphics/stk_mesh.hpp index 2248baaeb..f5df8c9b1 100644 --- a/src/graphics/stk_mesh.hpp +++ b/src/graphics/stk_mesh.hpp @@ -37,6 +37,8 @@ enum TransparentMaterial TM_DEFAULT, TM_ADDITIVE, TM_DISPLACEMENT, + TM_GHOST_KART, + TM_GHOST_KART_TANGENTS, TM_COUNT }; // TransparentMaterial @@ -206,6 +208,18 @@ class ListAdditiveTransparent : public MiscList {}; +// ---------------------------------------------------------------------------- +class ListGhostKart : public MiscList +{}; + +// ---------------------------------------------------------------------------- +class ListGhostKartTangents : public MiscList +{}; + // ---------------------------------------------------------------------------- class ListBlendTransparentFog : public MiscListgetMaterialFor(mb->getMaterial().getTexture(0), mb); if (mesh.m_render_info != NULL && mesh.m_render_info->isTransparent()) { - if (!immediate_draw) - TransparentMesh[TM_ADDITIVE].push_back(&mesh); + assert(!immediate_draw); + if (mesh.VAOType == video::EVT_TANGENTS) + TransparentMesh[TM_GHOST_KART_TANGENTS].push_back(&mesh); else - additive = true; + TransparentMesh[TM_GHOST_KART].push_back(&mesh); } else if (rnd->isTransparent()) { diff --git a/src/graphics/stk_scene_manager.cpp b/src/graphics/stk_scene_manager.cpp index 898539c9a..29a9fbdd6 100644 --- a/src/graphics/stk_scene_manager.cpp +++ b/src/graphics/stk_scene_manager.cpp @@ -344,6 +344,10 @@ handleSTKCommon(scene::ISceneNode *Node, std::vector *Immed for (GLMesh *mesh : node->TransparentMesh[TM_ADDITIVE]) pushVector(ListAdditiveTransparent::getInstance(), mesh, Node->getAbsoluteTransformation(), mesh->TextureMatrix); } + for (GLMesh *mesh : node->TransparentMesh[TM_GHOST_KART]) + pushVector(ListGhostKart::getInstance(), mesh, Node->getAbsoluteTransformation(), mesh->TextureMatrix); + for (GLMesh *mesh : node->TransparentMesh[TM_GHOST_KART_TANGENTS]) + pushVector(ListGhostKartTangents::getInstance(), mesh, Node->getAbsoluteTransformation(), mesh->TextureMatrix); for (GLMesh *mesh : node->TransparentMesh[TM_DISPLACEMENT]) pushVector(ListDisplacement::getInstance(), mesh, Node->getAbsoluteTransformation()); @@ -606,6 +610,8 @@ void IrrDriver::PrepareDrawCalls(scene::ICameraSceneNode *camnode) windDir = getWindDir(); ListBlendTransparent::getInstance()->clear(); ListAdditiveTransparent::getInstance()->clear(); + ListGhostKart::getInstance()->clear(); + ListGhostKartTangents::getInstance()->clear(); ListBlendTransparentFog::getInstance()->clear(); ListAdditiveTransparentFog::getInstance()->clear(); ListDisplacement::getInstance()->clear(); From af223ddf24a59431d226309e5ec7119a2a8060f9 Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 17 Oct 2016 01:03:10 +0800 Subject: [PATCH 323/350] Try to use sun color to determine custom alpha for ghost karts This may need more adjustments, also add custom_alpha uniform float to transparent fragment shader to remove duplicated shader --- data/shaders/ghost_karts.frag | 19 ------------------- data/shaders/transparent.frag | 3 ++- src/graphics/2dutils.cpp | 6 +++--- src/graphics/render_geometry.cpp | 8 ++++---- src/graphics/shaders.cpp | 11 +---------- src/graphics/shaders.hpp | 11 ++--------- src/graphics/stk_mesh.hpp | 9 +++++---- src/graphics/stk_mesh_scene_node.cpp | 2 +- src/graphics/stk_scene_manager.cpp | 18 ++++++++++++++---- 9 files changed, 32 insertions(+), 55 deletions(-) delete mode 100644 data/shaders/ghost_karts.frag diff --git a/data/shaders/ghost_karts.frag b/data/shaders/ghost_karts.frag deleted file mode 100644 index 18b707682..000000000 --- a/data/shaders/ghost_karts.frag +++ /dev/null @@ -1,19 +0,0 @@ -#ifdef Use_Bindless_Texture -layout(bindless_sampler) uniform sampler2D tex; -#else -uniform sampler2D tex; -#endif - -in vec2 uv; -in vec4 color; -out vec4 FragColor; - -void main() -{ - vec4 Color = texture(tex, uv); -#ifdef Use_Bindless_Texture - Color.xyz = pow(Color.xyz, vec3(2.2)); -#endif - Color.xyz *= pow(color.xyz, vec3(2.2)); - FragColor = vec4(Color.rgb * 0.5, 0.5); -} diff --git a/data/shaders/transparent.frag b/data/shaders/transparent.frag index 8d88d3687..2e04c5433 100644 --- a/data/shaders/transparent.frag +++ b/data/shaders/transparent.frag @@ -2,6 +2,7 @@ layout(bindless_sampler) uniform sampler2D tex; #else uniform sampler2D tex; +uniform float custom_alpha; #endif in vec2 uv; @@ -18,5 +19,5 @@ void main() Color.xyz *= pow(color.xyz, vec3(2.2)); Color.a *= color.a; // Premultiply alpha - FragColor = vec4(Color.rgb * Color.a, Color.a); + FragColor = vec4(Color.rgb * (Color.a * custom_alpha), Color.a * custom_alpha); } diff --git a/src/graphics/2dutils.cpp b/src/graphics/2dutils.cpp index 1419bba7d..69459bcde 100644 --- a/src/graphics/2dutils.cpp +++ b/src/graphics/2dutils.cpp @@ -34,14 +34,14 @@ // ============================================================================ -class Primitive2DList : public TextureShader +class Primitive2DList : public TextureShader { public: Primitive2DList() { loadProgram(OBJECT, GL_VERTEX_SHADER, "primitive2dlist.vert", GL_FRAGMENT_SHADER, "transparent.frag"); - assignUniforms(); + assignUniforms("custom_alpha"); assignSamplerNames(0, "tex", ST_BILINEAR_FILTERED); } // Primitive2DList }; //Primitive2DList @@ -703,7 +703,7 @@ void draw2DVertexPrimitiveList(video::ITexture *tex, const void* vertices, VertexUtils::bindVertexArrayAttrib(vType); Primitive2DList::getInstance()->use(); - Primitive2DList::getInstance()->setUniforms(); + Primitive2DList::getInstance()->setUniforms(1.0f); compressTexture(tex, false); Primitive2DList::getInstance()->setTextureUnits(getTextureGLuint(tex)); glDrawElements(GL_TRIANGLE_FAN, primitiveCount, GL_UNSIGNED_SHORT, 0); diff --git a/src/graphics/render_geometry.cpp b/src/graphics/render_geometry.cpp index b8c8da5d5..d9df940a4 100644 --- a/src/graphics/render_geometry.cpp +++ b/src/graphics/render_geometry.cpp @@ -1563,11 +1563,11 @@ void IrrDriver::renderTransparent() glEnable(GL_BLEND); glBlendEquation(GL_FUNC_ADD); glEnable(GL_CULL_FACE); - renderTransparenPass( + renderTransparenPass( TexUnits(RenderGeometry::TexUnit(0, true)), ListGhostKart::getInstance()); - renderTransparenPass( + renderTransparenPass( TexUnits(RenderGeometry::TexUnit(0, true)), ListGhostKartTangents::getInstance()); @@ -1596,11 +1596,11 @@ void IrrDriver::renderTransparent() { glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); renderTransparenPass( + video::EVT_STANDARD, 3, 2, 1>( TexUnits(RenderGeometry::TexUnit(0, true)), ListBlendTransparent::getInstance()); glBlendFunc(GL_ONE, GL_ONE); - renderTransparenPass( + renderTransparenPass( TexUnits(RenderGeometry::TexUnit(0, true)), ListAdditiveTransparent::getInstance()); } diff --git a/src/graphics/shaders.cpp b/src/graphics/shaders.cpp index 9b5fca4ba..205493df5 100644 --- a/src/graphics/shaders.cpp +++ b/src/graphics/shaders.cpp @@ -365,19 +365,10 @@ Shaders::TransparentShader::TransparentShader() { loadProgram(OBJECT, GL_VERTEX_SHADER, "object_pass.vert", GL_FRAGMENT_SHADER, "transparent.frag"); - assignUniforms("ModelMatrix", "TextureMatrix"); + assignUniforms("ModelMatrix", "TextureMatrix", "custom_alpha"); assignSamplerNames(0, "tex", ST_TRILINEAR_ANISOTROPIC_FILTERED); } // TransparentShader -// ============================================================================ -Shaders::GhostKartsShader::GhostKartsShader() -{ - loadProgram(OBJECT, GL_VERTEX_SHADER, "object_pass.vert", - GL_FRAGMENT_SHADER, "ghost_karts.frag"); - assignUniforms("ModelMatrix", "TextureMatrix"); - assignSamplerNames(0, "tex", ST_TRILINEAR_ANISOTROPIC_FILTERED); -} // GhostKartsShader - // ============================================================================ Shaders::TransparentFogShader::TransparentFogShader() { diff --git a/src/graphics/shaders.hpp b/src/graphics/shaders.hpp index 44e629c55..7433329d4 100644 --- a/src/graphics/shaders.hpp +++ b/src/graphics/shaders.hpp @@ -120,20 +120,13 @@ public: // ======================================================================== class TransparentShader : public TextureShader + core::matrix4, core::matrix4, + float > { public: TransparentShader(); }; // TransparentShader - // ======================================================================== - class GhostKartsShader : public TextureShader - { - public: - GhostKartsShader(); - }; // TransparentShader - // ======================================================================== class TransparentFogShader : public TextureShader, public std::vector > // ---------------------------------------------------------------------------- class ListBlendTransparent : public MiscList + core::matrix4, core::matrix4, + float> {}; // ---------------------------------------------------------------------------- class ListAdditiveTransparent : public MiscList + core::matrix4, float> {}; // ---------------------------------------------------------------------------- class ListGhostKart : public MiscList + core::matrix4, float> {}; // ---------------------------------------------------------------------------- class ListGhostKartTangents : public MiscList + core::matrix4, float> {}; // ---------------------------------------------------------------------------- diff --git a/src/graphics/stk_mesh_scene_node.cpp b/src/graphics/stk_mesh_scene_node.cpp index 4d01aa8ad..fee53ee2d 100644 --- a/src/graphics/stk_mesh_scene_node.cpp +++ b/src/graphics/stk_mesh_scene_node.cpp @@ -568,7 +568,7 @@ void STKMeshSceneNode::render() #endif Shaders::TransparentShader::getInstance()->setTextureUnits(getTextureGLuint(mesh.textures[0])); - Shaders::TransparentShader::getInstance()->setUniforms(AbsoluteTransformation, mesh.TextureMatrix); + Shaders::TransparentShader::getInstance()->setUniforms(AbsoluteTransformation, mesh.TextureMatrix, 1.0f); assert(mesh.vao); glBindVertexArray(mesh.vao); glDrawElements(ptype, count, itype, 0); diff --git a/src/graphics/stk_scene_manager.cpp b/src/graphics/stk_scene_manager.cpp index 29a9fbdd6..95d1fb01a 100644 --- a/src/graphics/stk_scene_manager.cpp +++ b/src/graphics/stk_scene_manager.cpp @@ -340,14 +340,24 @@ handleSTKCommon(scene::ISceneNode *Node, std::vector *Immed else { for (GLMesh *mesh : node->TransparentMesh[TM_DEFAULT]) - pushVector(ListBlendTransparent::getInstance(), mesh, Node->getAbsoluteTransformation(), mesh->TextureMatrix); + pushVector(ListBlendTransparent::getInstance(), mesh, Node->getAbsoluteTransformation(), mesh->TextureMatrix, 1.0f); for (GLMesh *mesh : node->TransparentMesh[TM_ADDITIVE]) - pushVector(ListAdditiveTransparent::getInstance(), mesh, Node->getAbsoluteTransformation(), mesh->TextureMatrix); + pushVector(ListAdditiveTransparent::getInstance(), mesh, Node->getAbsoluteTransformation(), mesh->TextureMatrix, 1.0f); } + + // Use sun color to determine custom alpha for ghost karts + float custom_alpha = 1.0f; + if (World::getWorld()) + { + const video::SColor& c = World::getWorld()->getTrack()->getSunColor(); + float y = 0.2126f * c.getRed() + 0.7152f * c.getGreen() + 0.0722f * c.getBlue(); + custom_alpha = y > 128.0f ? 0.5f : 0.2f; + } + for (GLMesh *mesh : node->TransparentMesh[TM_GHOST_KART]) - pushVector(ListGhostKart::getInstance(), mesh, Node->getAbsoluteTransformation(), mesh->TextureMatrix); + pushVector(ListGhostKart::getInstance(), mesh, Node->getAbsoluteTransformation(), mesh->TextureMatrix, custom_alpha); for (GLMesh *mesh : node->TransparentMesh[TM_GHOST_KART_TANGENTS]) - pushVector(ListGhostKartTangents::getInstance(), mesh, Node->getAbsoluteTransformation(), mesh->TextureMatrix); + pushVector(ListGhostKartTangents::getInstance(), mesh, Node->getAbsoluteTransformation(), mesh->TextureMatrix, custom_alpha); for (GLMesh *mesh : node->TransparentMesh[TM_DISPLACEMENT]) pushVector(ListDisplacement::getInstance(), mesh, Node->getAbsoluteTransformation()); From caf095ed60270c116adb0ee0f8bd382e5df9d24f Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 17 Oct 2016 09:13:07 +0800 Subject: [PATCH 324/350] Add improvements from auria and devee --- src/graphics/render_geometry.cpp | 1 + src/graphics/stk_mesh_scene_node.cpp | 2 +- src/graphics/stk_scene_manager.cpp | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/graphics/render_geometry.cpp b/src/graphics/render_geometry.cpp index d9df940a4..2842d358d 100644 --- a/src/graphics/render_geometry.cpp +++ b/src/graphics/render_geometry.cpp @@ -1563,6 +1563,7 @@ void IrrDriver::renderTransparent() glEnable(GL_BLEND); glBlendEquation(GL_FUNC_ADD); glEnable(GL_CULL_FACE); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); renderTransparenPass( TexUnits(RenderGeometry::TexUnit(0, true)), ListGhostKart::getInstance()); diff --git a/src/graphics/stk_mesh_scene_node.cpp b/src/graphics/stk_mesh_scene_node.cpp index fee53ee2d..d48c98441 100644 --- a/src/graphics/stk_mesh_scene_node.cpp +++ b/src/graphics/stk_mesh_scene_node.cpp @@ -207,7 +207,7 @@ void STKMeshSceneNode::updateNoGL() GLMesh &mesh = GLmeshes[i]; Material* material = material_manager->getMaterialFor(mb->getMaterial().getTexture(0), mb); - if (mesh.m_render_info != NULL && mesh.m_render_info->isTransparent()) + if (mesh.m_render_info != NULL && mesh.m_render_info->isTransparent() && !rnd->isTransparent()) { assert(!immediate_draw); if (mesh.VAOType == video::EVT_TANGENTS) diff --git a/src/graphics/stk_scene_manager.cpp b/src/graphics/stk_scene_manager.cpp index 95d1fb01a..5ddfa53f6 100644 --- a/src/graphics/stk_scene_manager.cpp +++ b/src/graphics/stk_scene_manager.cpp @@ -351,7 +351,7 @@ handleSTKCommon(scene::ISceneNode *Node, std::vector *Immed { const video::SColor& c = World::getWorld()->getTrack()->getSunColor(); float y = 0.2126f * c.getRed() + 0.7152f * c.getGreen() + 0.0722f * c.getBlue(); - custom_alpha = y > 128.0f ? 0.5f : 0.2f; + custom_alpha = y > 128.0f ? 0.5f : 0.35f; } for (GLMesh *mesh : node->TransparentMesh[TM_GHOST_KART]) From 5502fa4ebeae42833a71e0fcdaf10770de08e08c Mon Sep 17 00:00:00 2001 From: deve Date: Mon, 17 Oct 2016 08:23:27 +0200 Subject: [PATCH 325/350] Declare custom alpha also when bindless sampler is enabled --- data/shaders/transparent.frag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/shaders/transparent.frag b/data/shaders/transparent.frag index 2e04c5433..03250aa21 100644 --- a/data/shaders/transparent.frag +++ b/data/shaders/transparent.frag @@ -2,8 +2,8 @@ layout(bindless_sampler) uniform sampler2D tex; #else uniform sampler2D tex; -uniform float custom_alpha; #endif +uniform float custom_alpha; in vec2 uv; in vec4 color; From eddf39b8a1b690b0ec9835374bd36e69a84b9982 Mon Sep 17 00:00:00 2001 From: LoadingPleaseWait Date: Mon, 17 Oct 2016 03:36:29 -0500 Subject: [PATCH 326/350] Remove duplicate include statements from kart.cpp --- src/karts/kart.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 5efa6f4c6..c2dd039f7 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -90,13 +90,6 @@ #include #include -#include -#include -#include // for min and max - -#include -#include - #if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) // Disable warning for using 'this' in base member initializer list From 513e32d9fff28894ba0825b9587392b6ae01f1e0 Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Mon, 17 Oct 2016 19:09:01 -0400 Subject: [PATCH 327/350] Fix bug that could cause reversible textures to be mirrorred twice when driving the track in reverse (see #2167) --- src/graphics/material.cpp | 18 +++++++++++------- src/graphics/material.hpp | 6 ++++++ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/graphics/material.cpp b/src/graphics/material.cpp index 98a4d705b..97f9e7c4b 100644 --- a/src/graphics/material.cpp +++ b/src/graphics/material.cpp @@ -990,14 +990,18 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m if (race_manager->getReverseTrack() && m_mirror_axis_when_reverse != ' ') { - //irr::video::S3DVertex* mbVertices = (video::S3DVertex*)mb->getVertices(); - for (unsigned int i = 0; i < mb->getVertexCount(); i++) + if (m_mirrorred_mesh_buffers.find((void*)mb) == m_mirrorred_mesh_buffers.end()) { - core::vector2df &tc = mb->getTCoords(i); - if (m_mirror_axis_when_reverse == 'V') - tc.Y = 1 - tc.Y; - else - tc.X = 1 - tc.X; + m_mirrorred_mesh_buffers[(void*)mb] = true; + //irr::video::S3DVertex* mbVertices = (video::S3DVertex*)mb->getVertices(); + for (unsigned int i = 0; i < mb->getVertexCount(); i++) + { + core::vector2df &tc = mb->getTCoords(i); + if (m_mirror_axis_when_reverse == 'V') + tc.Y = 1 - tc.Y; + else + tc.X = 1 - tc.X; + } } } // reverse track and texture needs mirroring diff --git a/src/graphics/material.hpp b/src/graphics/material.hpp index 910e60c12..f191a80b0 100644 --- a/src/graphics/material.hpp +++ b/src/graphics/material.hpp @@ -162,6 +162,12 @@ private: * the direction. */ char m_mirror_axis_when_reverse; + /** + * Associated with m_mirror_axis_when_reverse, to avoid mirroring the same material twice + * (setAllMaterialFlags can be called multiple times on the same mesh buffer) + */ + std::map m_mirrorred_mesh_buffers; + ParticleKind* m_particles_effects[EMIT_KINDS_COUNT]; /** For normal maps */ From 9d1b115e591f90ae4d98201c07d8864a5b2b10fc Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Mon, 17 Oct 2016 19:15:28 -0400 Subject: [PATCH 328/350] Don't call OpenAL's position method when audio is disabled --- src/audio/sfx_openal.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/audio/sfx_openal.cpp b/src/audio/sfx_openal.cpp index 3eb1c3eb4..174c60db3 100644 --- a/src/audio/sfx_openal.cpp +++ b/src/audio/sfx_openal.cpp @@ -424,7 +424,7 @@ void SFXOpenAL::setPosition(const Vec3 &position) { // Don't send a position command to the thread if the sound is not playing // (or sfx disabled or the sound was not initialised correctly) -// if (m_status != SFX_PLAYING|| !SFXManager::get()->sfxAllowed()) return; + if (!SFXManager::get()->sfxAllowed()) return; SFXManager::get()->queue(SFXManager::SFX_POSITION, this, position); } // setPosition From 8c0fe53ff2803705513143a06fc9e2a7931bb776 Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Mon, 17 Oct 2016 19:46:21 -0400 Subject: [PATCH 329/350] Fix texture mirroring on alpha blend/test shaders. See #2167 --- src/graphics/material.cpp | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/graphics/material.cpp b/src/graphics/material.cpp index 97f9e7c4b..d73d5ebad 100644 --- a/src/graphics/material.cpp +++ b/src/graphics/material.cpp @@ -740,6 +740,26 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m m->setTexture(7, colorization_mask_tex); } + + if (race_manager->getReverseTrack() && + m_mirror_axis_when_reverse != ' ') + { + if (m_mirrorred_mesh_buffers.find((void*)mb) == m_mirrorred_mesh_buffers.end()) + { + m_mirrorred_mesh_buffers[(void*)mb] = true; + //irr::video::S3DVertex* mbVertices = (video::S3DVertex*)mb->getVertices(); + for (unsigned int i = 0; i < mb->getVertexCount(); i++) + { + core::vector2df &tc = mb->getTCoords(i); + if (m_mirror_axis_when_reverse == 'V') + tc.Y = 1 - tc.Y; + else + tc.X = 1 - tc.X; + } + } + } // reverse track and texture needs mirroring + + switch (m_shader_type) { case SHADERTYPE_SOLID_UNLIT: @@ -986,25 +1006,6 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m } #endif - - if (race_manager->getReverseTrack() && - m_mirror_axis_when_reverse != ' ') - { - if (m_mirrorred_mesh_buffers.find((void*)mb) == m_mirrorred_mesh_buffers.end()) - { - m_mirrorred_mesh_buffers[(void*)mb] = true; - //irr::video::S3DVertex* mbVertices = (video::S3DVertex*)mb->getVertices(); - for (unsigned int i = 0; i < mb->getVertexCount(); i++) - { - core::vector2df &tc = mb->getTCoords(i); - if (m_mirror_axis_when_reverse == 'V') - tc.Y = 1 - tc.Y; - else - tc.X = 1 - tc.X; - } - } - } // reverse track and texture needs mirroring - } // setMaterialProperties //----------------------------------------------------------------------------- From 9aa92cfe30052cf46284a15772e3a207382823d7 Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 18 Oct 2016 09:55:30 +0800 Subject: [PATCH 330/350] Fix profiling mode framerate regression --- src/main_loop.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main_loop.cpp b/src/main_loop.cpp index 2810f68d5..29d9a1fad 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -222,11 +222,14 @@ void MainLoop::run() m_prev_time = m_curr_time; float dt = getLimitedDt(); - // Render the previous frame, and also handle all user input. - PROFILER_PUSH_CPU_MARKER("IrrDriver update", 0x00, 0x00, 0x7F); - irr_driver->update(dt); - PROFILER_POP_CPU_MARKER(); + if (!m_abort && !ProfileWorld::isNoGraphics()) + { + // Render the previous frame, and also handle all user input. + PROFILER_PUSH_CPU_MARKER("IrrDriver update", 0x00, 0x00, 0x7F); + irr_driver->update(dt); + PROFILER_POP_CPU_MARKER(); + } if (World::getWorld()) // race is active if world exists { From f1caba79328f27d21e09fbff91918bdd24516576 Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 18 Oct 2016 10:33:27 +0800 Subject: [PATCH 331/350] Make ArenaAI reverse more naturally --- src/karts/controller/arena_ai.cpp | 41 +++++++++---------------------- src/karts/controller/arena_ai.hpp | 4 --- 2 files changed, 11 insertions(+), 34 deletions(-) diff --git a/src/karts/controller/arena_ai.cpp b/src/karts/controller/arena_ai.cpp index 0785829ff..4f33f1d6a 100644 --- a/src/karts/controller/arena_ai.cpp +++ b/src/karts/controller/arena_ai.cpp @@ -48,7 +48,6 @@ void ArenaAI::reset() m_target_node = Graph::UNKNOWN_SECTOR; m_current_forward_node = Graph::UNKNOWN_SECTOR; m_current_forward_point = Vec3(0, 0, 0); - m_adjusting_side = false; m_closest_kart = NULL; m_closest_kart_node = Graph::UNKNOWN_SECTOR; m_closest_kart_point = Vec3(0, 0, 0); @@ -81,6 +80,10 @@ void ArenaAI::update(float dt) m_controls->setLookBack(false); m_controls->setNitro(false); + // Let the function below to reset it later + m_controls->setAccel(0.0f); + m_controls->setBrake(false); + // Don't do anything if there is currently a kart animations shown. if (m_kart->getKartAnimation()) { @@ -249,8 +252,6 @@ void ArenaAI::configSteering() #endif if (m_target_point_lc.z() < 0) { - // Local coordinate z < 0 == target point is behind - m_adjusting_side = m_target_point_lc.x() < 0; m_is_uturn = true; } else @@ -273,7 +274,6 @@ void ArenaAI::configSteering() #endif if (m_target_point_lc.z() < 0) { - m_adjusting_side = m_target_point_lc.x() < 0; m_is_uturn = true; } else @@ -323,9 +323,6 @@ void ArenaAI::checkIfStuck(const float dt) */ void ArenaAI::configSpeed() { - m_controls->setAccel(0.0f); - m_controls->setBrake(false); - // A kart will not brake when the speed is already slower than this // value. This prevents a kart from going too slow (or even backwards) // in tight curves. @@ -350,24 +347,14 @@ void ArenaAI::configSpeed() //----------------------------------------------------------------------------- void ArenaAI::doUTurn(const float dt) { - const float turn_side = (m_adjusting_side ? 1.0f : -1.0f); - - if (fabsf(m_kart->getSpeed()) > - (m_kart->getKartProperties()->getEngineMaxSpeed() / 5) - && m_kart->getSpeed() < 0) // Try to emulate reverse like human players - m_controls->setAccel(-0.06f); - else - m_controls->setAccel(-5.0f); - - if (m_time_since_uturn >= - (m_cur_difficulty == RaceManager::DIFFICULTY_EASY ? 2.0f : 1.5f)) - setSteering(-(turn_side), dt); // Preventing keep going around circle - else - setSteering(turn_side, dt); + float turn_angle = atan2f(m_target_point_lc.x(), + fabsf(m_target_point_lc.z())); + m_controls->setBrake(true); + setSteering(-turn_angle, dt); m_time_since_uturn += dt; - if (m_target_point_lc.z() > 0 || m_time_since_uturn > - (m_cur_difficulty == RaceManager::DIFFICULTY_EASY ? 3.5f : 3.0f)) + if ((m_target_point_lc.z() > 0 && fabsf(turn_angle) < 0.2f) || + m_time_since_uturn > 1.5f) { // End U-turn until target point is in front of this AI m_is_uturn = false; @@ -385,13 +372,7 @@ bool ArenaAI::gettingUnstuck(const float dt) resetAfterStop(); setSteering(0.0f, dt); - - if (fabsf(m_kart->getSpeed()) > - (m_kart->getKartProperties()->getEngineMaxSpeed() / 5) - && m_kart->getSpeed() < 0) - m_controls->setAccel(-0.06f); - else - m_controls->setAccel(-4.0f); + m_controls->setBrake(true); m_time_since_reversing += dt; diff --git a/src/karts/controller/arena_ai.hpp b/src/karts/controller/arena_ai.hpp index 40adaaac5..c0e0db2fe 100644 --- a/src/karts/controller/arena_ai.hpp +++ b/src/karts/controller/arena_ai.hpp @@ -67,10 +67,6 @@ protected: void collectItemInArena(Vec3*, int*) const; private: - /** Used by handleArenaUTurn, it tells whether to do left or right - * turning when steering is overridden. */ - bool m_adjusting_side; - Vec3 m_target_point_lc; /** Indicates that the kart is currently stuck, and m_time_since_reversing is From 8eb8e805eb632f65f16835806e9d3b5e5e4c5d2c Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 18 Oct 2016 14:18:51 +0800 Subject: [PATCH 332/350] Fix dropping of normal map wheel in battle mode --- src/tracks/track_object_presentation.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/tracks/track_object_presentation.cpp b/src/tracks/track_object_presentation.cpp index 3c98ca224..2dbfc4b6c 100644 --- a/src/tracks/track_object_presentation.cpp +++ b/src/tracks/track_object_presentation.cpp @@ -419,9 +419,21 @@ TrackObjectPresentationMesh::TrackObjectPresentationMesh( if (file_manager->fileExists(model_file)) { if (animated) + { m_mesh = irr_driver->getAnimatedMesh(model_file); + scene::ISkinnedMesh* sm = + dynamic_cast(m_mesh); + if (sm) + { + MeshTools::createSkinnedMeshWithTangents(sm, + &MeshTools::isNormalMap); + } + } else - m_mesh = irr_driver->getMesh(model_file); + { + m_mesh = MeshTools::createMeshWithTangents( + irr_driver->getMesh(model_file), &MeshTools::isNormalMap); + } } if (!m_mesh) From b12f7a1079f83493aafef8e2580eea47ef410c1b Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 19 Oct 2016 00:37:29 +0800 Subject: [PATCH 333/350] Fix shaking u-turn --- src/karts/controller/arena_ai.cpp | 25 ++++++++++++++++--------- src/karts/controller/arena_ai.hpp | 2 ++ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/karts/controller/arena_ai.cpp b/src/karts/controller/arena_ai.cpp index fccfd3c96..931ce326e 100644 --- a/src/karts/controller/arena_ai.cpp +++ b/src/karts/controller/arena_ai.cpp @@ -56,6 +56,7 @@ void ArenaAI::reset() m_mini_skid = false; m_target_point = Vec3(0, 0, 0); m_target_point_lc = Vec3(0, 0, 0); + m_reverse_point = Vec3(0, 0, 0); m_time_since_last_shot = 0.0f; m_time_since_driving = 0.0f; m_time_since_off_road = 0.0f; @@ -83,6 +84,7 @@ void ArenaAI::update(float dt) // Let the function below to reset it later m_controls->setAccel(0.0f); m_controls->setBrake(false); + m_mini_skid = false; // Don't do anything if there is currently a kart animations shown. if (m_kart->getKartAnimation()) @@ -123,9 +125,16 @@ void ArenaAI::update(float dt) // After found target, convert it to local coordinate, used for skidding or // u-turn - m_target_point_lc = m_kart->getTrans().inverse()(m_target_point); - doSkiddingTest(); - configSteering(); + if (!m_is_uturn) + { + m_target_point_lc = m_kart->getTrans().inverse()(m_target_point); + doSkiddingTest(); + configSteering(); + } + else + { + m_target_point_lc = m_kart->getTrans().inverse()(m_reverse_point); + } useItems(dt); if (m_kart->getSpeed() > 15.0f && !m_is_uturn && m_turn_radius > 30.0f && @@ -252,6 +261,7 @@ void ArenaAI::configSteering() if (m_target_point_lc.z() < 0) { m_is_uturn = true; + m_reverse_point = m_target_point; } else { @@ -274,6 +284,7 @@ void ArenaAI::configSteering() if (m_target_point_lc.z() < 0) { m_is_uturn = true; + m_reverse_point = m_target_point; } else { @@ -349,7 +360,7 @@ void ArenaAI::doUTurn(const float dt) float turn_angle = atan2f(m_target_point_lc.x(), fabsf(m_target_point_lc.z())); m_controls->setBrake(true); - setSteering(-turn_angle, dt); + setSteering(turn_angle > 0.0f ? -1.0f : 1.0f, dt); m_time_since_uturn += dt; if ((m_target_point_lc.z() > 0 && fabsf(turn_angle) < 0.2f) || @@ -359,6 +370,7 @@ void ArenaAI::doUTurn(const float dt) m_is_uturn = false; m_time_since_uturn = 0.0f; m_time_since_driving = 0.0f; + m_reverse_point = Vec3(0, 0, 0); } else m_is_uturn = true; @@ -604,11 +616,6 @@ void ArenaAI::collectItemInArena(Vec3* aim_point, int* target_node) const //----------------------------------------------------------------------------- void ArenaAI::doSkiddingTest() { - m_mini_skid = false; - - // No skidding when u-turn - if (m_is_uturn) return; - // Skid when close to target, but not straight ahead, in front of it, same // steering side and with suitable difficulties. const float abs_angle = atan2f(fabsf(m_target_point_lc.x()), diff --git a/src/karts/controller/arena_ai.hpp b/src/karts/controller/arena_ai.hpp index de1b3a529..68cc29fb7 100644 --- a/src/karts/controller/arena_ai.hpp +++ b/src/karts/controller/arena_ai.hpp @@ -70,6 +70,8 @@ protected: private: Vec3 m_target_point_lc; + Vec3 m_reverse_point; + /** Indicates that the kart is currently stuck, and m_time_since_reversing is * counting down. */ bool m_is_stuck; From 021509387d8ac54073f4297e9c67528b7fa6b96e Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 20 Oct 2016 16:40:20 +0800 Subject: [PATCH 334/350] Add doxygen for Arena and Battle AI Plus some coding-style cleanup --- src/karts/controller/ai_base_controller.hpp | 2 + src/karts/controller/arena_ai.cpp | 45 ++++++++---- src/karts/controller/arena_ai.hpp | 81 ++++++++++++++++++--- src/karts/controller/battle_ai.cpp | 62 ++++++++++------ src/karts/controller/battle_ai.hpp | 14 +++- src/karts/controller/soccer_ai.cpp | 6 +- src/karts/controller/soccer_ai.hpp | 2 +- 7 files changed, 159 insertions(+), 53 deletions(-) diff --git a/src/karts/controller/ai_base_controller.hpp b/src/karts/controller/ai_base_controller.hpp index 82e07f542..20647e28d 100644 --- a/src/karts/controller/ai_base_controller.hpp +++ b/src/karts/controller/ai_base_controller.hpp @@ -75,6 +75,8 @@ protected: float *radius) const; virtual void update (float delta); virtual void setSteering (float angle, float dt); + // ------------------------------------------------------------------------ + /** Return true if AI can skid now. */ virtual bool canSkid(float steer_fraction) = 0; public: diff --git a/src/karts/controller/arena_ai.cpp b/src/karts/controller/arena_ai.cpp index 931ce326e..b667d17fa 100644 --- a/src/karts/controller/arena_ai.cpp +++ b/src/karts/controller/arena_ai.cpp @@ -74,6 +74,7 @@ void ArenaAI::reset() /** This is the main entry point for the AI. * It is called once per frame for each AI and determines the behaviour of * the AI, e.g. steering, accelerating/braking, firing. + * \param dt Time step size. */ void ArenaAI::update(float dt) { @@ -170,10 +171,7 @@ bool ArenaAI::updateAimingPosition(Vec3* target_point) m_debug_sphere_next->setVisible(false); #endif - // Notice: we use the point ahead of kart to determine next node, - // to compensate the time difference between steering - m_current_forward_point = - m_kart->getTrans()(Vec3(0, 0, m_kart->getKartLength())); + m_current_forward_point = m_kart->getTrans()(Vec3(0, 0, m_kart_length)); m_turn_radius = 0.0f; std::vector* test_nodes = NULL; @@ -238,7 +236,7 @@ bool ArenaAI::updateAimingPosition(Vec3* target_point) } // updateAimingPosition //----------------------------------------------------------------------------- -/** This function config the steering of AI. +/** This function config the steering (\ref m_steering_angle) of AI. */ void ArenaAI::configSteering() { @@ -294,6 +292,11 @@ void ArenaAI::configSteering() } // configSteering //----------------------------------------------------------------------------- +/** Determine whether AI is stuck, by checking if it stays on the same node for + * a long period of time (see \ref m_on_node), or \ref isStuck() is true. + * \param dt Time step size. + * \return True if AI is stuck + */ void ArenaAI::checkIfStuck(const float dt) { if (m_is_stuck) return; @@ -312,8 +315,7 @@ void ArenaAI::checkIfStuck(const float dt) && m_on_node.size() < 2 && !m_is_uturn && fabsf(m_kart->getSpeed()) < 3.0f) || isStuck() == true) { - // Check whether a kart stay on the same node for a period of time - // Or crashed 3 times + // AI is stuck, reset now and try to get unstuck at next frame m_on_node.clear(); m_time_since_driving = 0.0f; AIBaseController::reset(); @@ -355,6 +357,8 @@ void ArenaAI::configSpeed() } // configSpeed //----------------------------------------------------------------------------- +/** Make AI reverse so that it faces in front of the last target point. + */ void ArenaAI::doUTurn(const float dt) { float turn_angle = atan2f(m_target_point_lc.x(), @@ -377,6 +381,10 @@ void ArenaAI::doUTurn(const float dt) } // doUTurn //----------------------------------------------------------------------------- +/** Function to let AI get unstuck. + * \param dt Time step size. + * \return True if getting stuck is needed to be done. + */ bool ArenaAI::gettingUnstuck(const float dt) { if (!m_is_stuck || m_is_uturn) return false; @@ -398,6 +406,10 @@ bool ArenaAI::gettingUnstuck(const float dt) } // gettingUnstuck //----------------------------------------------------------------------------- +/** Determine how AI should use its item, different \ref m_cur_difficulty will + * have a corresponding strategy. + * \param dt Time step size. + */ void ArenaAI::useItems(const float dt) { m_controls->setFire(false); @@ -406,7 +418,7 @@ void ArenaAI::useItems(const float dt) return; // Find a closest kart again, this time we ignore difficulty - findClosestKart(false/*use_difficulty*/, false/*find_sta*/); + findClosestKart(false/*consider_difficulty*/, false/*find_sta*/); if (!m_closest_kart) return; Vec3 closest_kart_point_lc = @@ -558,7 +570,12 @@ void ArenaAI::useItems(const float dt) } // useItems //----------------------------------------------------------------------------- -void ArenaAI::collectItemInArena(Vec3* aim_point, int* target_node) const +/** Try to collect item in arena, if no suitable item is found, like they are + * swapped, it will follow closest kart instead. + * \param[out] aim_point Location of item. + * \param[out] target_node The node which item lied on. + */ +void ArenaAI::tryCollectItem(Vec3* aim_point, int* target_node) const { float distance = 999999.9f; Item* selected = (*target_node == Graph::UNKNOWN_SECTOR ? NULL : @@ -611,13 +628,15 @@ void ArenaAI::collectItemInArena(Vec3* aim_point, int* target_node) const *aim_point = m_closest_kart_point; *target_node = m_closest_kart_node; } -} // collectItemInArena +} // tryCollectItem //----------------------------------------------------------------------------- +/** Determine if AI should skid: When it's close to target, but not straight + * ahead, in front of it, same steering side and with suitable difficulties + * which are in expert and supertux only. + */ void ArenaAI::doSkiddingTest() { - // Skid when close to target, but not straight ahead, in front of it, same - // steering side and with suitable difficulties. const float abs_angle = atan2f(fabsf(m_target_point_lc.x()), fabsf(m_target_point_lc.z())); if ((m_cur_difficulty == RaceManager::DIFFICULTY_HARD || @@ -636,7 +655,7 @@ void ArenaAI::doSkiddingTest() /** Determine if the path to target needs to be changed to avoid bad items, it * will also set the turn radius based on the new path if necessary. * \param forward Forward node of current AI position. - * \param path Default path to target. + * \param[in,out] path Default path to follow, will be changed if needed. */ void ArenaAI::determinePath(int forward, std::vector* path) { diff --git a/src/karts/controller/arena_ai.hpp b/src/karts/controller/arena_ai.hpp index 68cc29fb7..fee41d97b 100644 --- a/src/karts/controller/arena_ai.hpp +++ b/src/karts/controller/arena_ai.hpp @@ -32,7 +32,6 @@ class ArenaGraph; namespace irr { namespace scene { class ISceneNode; } - namespace video { class ITexture; } } /** A base class for AI that use navmesh to work. @@ -41,12 +40,16 @@ namespace irr class ArenaAI : public AIBaseController { protected: + /** Pointer to the \ref ArenaGraph. */ ArenaGraph* m_graph; /** Pointer to the closest kart around this kart. */ AbstractKart *m_closest_kart; + /** The \ref ArenaNode at which the closest kart located on. */ int m_closest_kart_node; + + /** The closest kart location. */ Vec3 m_closest_kart_point; /** Holds the current difficulty. */ @@ -55,25 +58,39 @@ protected: /** For debugging purpose: a sphere indicating where the AI * is targeting at. */ irr::scene::ISceneNode *m_debug_sphere; + + /** For debugging purpose: a sphere indicating where the first + * turning corner is located. */ irr::scene::ISceneNode *m_debug_sphere_next; - /** The node(quad) at which the target point lies in. */ + /** The \ref ArenaNode at which the target point located on. */ int m_target_node; - /** The target point. */ + /** The coordinates of target point. */ Vec3 m_target_point; + /** True if AI can skid, currently only do when close to target, see + * \ref doSkiddingTest(). */ bool m_mini_skid; - void collectItemInArena(Vec3*, int*) const; - virtual void findClosestKart(bool use_difficulty, bool find_sta) = 0; + // ------------------------------------------------------------------------ + void tryCollectItem(Vec3* aim_point, int* target_node) const; + // ------------------------------------------------------------------------ + /** Find the closest kart around this AI, implemented by sub-class. + * \param consider_difficulty If take current difficulty into account. + * \param find_sta If find \ref SpareTireAI only. */ + virtual void findClosestKart(bool consider_difficulty, bool find_sta) = 0; + private: + /** Local coordinates of current target point. */ Vec3 m_target_point_lc; + /** Save the last target point before reversing, so AI will end reversing + * until facing in front of it. */ Vec3 m_reverse_point; - /** Indicates that the kart is currently stuck, and m_time_since_reversing is - * counting down. */ + /** Indicates that the kart is currently stuck, and m_time_since_reversing + * is counting down. */ bool m_is_stuck; /** Indicates that the kart need a uturn to reach a node behind, and @@ -99,39 +116,79 @@ private: /** This is a timer that counts when the kart start going off road. */ float m_time_since_off_road; + /** Used to determine braking and nitro usage. */ float m_turn_radius; + /** Used to determine if skidding can be done. */ float m_steering_angle; + /** The point in front of the AI which distance is \ref m_kart_length, used + * to compensate the time difference between steering when finding next + * node. */ Vec3 m_current_forward_point; + /** The \ref ArenaNode at which the forward point located on. */ int m_current_forward_node; void configSpeed(); + // ------------------------------------------------------------------------ void configSteering(); + // ------------------------------------------------------------------------ void checkIfStuck(const float dt); + // ------------------------------------------------------------------------ void determinePath(int forward, std::vector* path); + // ------------------------------------------------------------------------ void doSkiddingTest(); + // ------------------------------------------------------------------------ void doUTurn(const float dt); + // ------------------------------------------------------------------------ bool gettingUnstuck(const float dt); + // ------------------------------------------------------------------------ bool updateAimingPosition(Vec3* target_point); + // ------------------------------------------------------------------------ void useItems(const float dt); + // ------------------------------------------------------------------------ virtual bool canSkid(float steer_fraction) OVERRIDE - { return m_mini_skid; } + { return m_mini_skid; } + // ------------------------------------------------------------------------ + /** Find a suitable target for this frame, implemented by sub-class. */ virtual void findTarget() = 0; - virtual bool forceBraking() { return false; } + // ------------------------------------------------------------------------ + /** If true, AI will always try to brake for this frame. */ + virtual bool forceBraking() { return false; } + // ------------------------------------------------------------------------ + /** Return the current \ref ArenaNode the AI located on. */ virtual int getCurrentNode() const = 0; + // ------------------------------------------------------------------------ + /** Return the distance based on graph distance matrix to any kart. + * \param kart \ref AbstractKart to check. */ virtual float getKartDistance(const AbstractKart* kart) const = 0; - virtual bool ignorePathFinding() { return false; } + // ------------------------------------------------------------------------ + /** If true, AI will drive directly to target without path finding. */ + virtual bool ignorePathFinding() { return false; } + // ------------------------------------------------------------------------ + /** If true, AI will stop moving. */ virtual bool isWaiting() const = 0; + // ------------------------------------------------------------------------ + /** If true, AI stays on the \ref ArenaNode correctly, otherwise + * \ref RescueAnimation will be done after sometime. */ virtual bool isKartOnRoad() const = 0; - virtual void resetAfterStop() {}; + // ------------------------------------------------------------------------ + /** Overridden if any action is needed to be done when AI stopped + * moving or changed driving direction. */ + virtual void resetAfterStop() {} + public: ArenaAI(AbstractKart *kart); - virtual ~ArenaAI() {}; + // ------------------------------------------------------------------------ + virtual ~ArenaAI() {} + // ------------------------------------------------------------------------ virtual void update (float delta) OVERRIDE; + // ------------------------------------------------------------------------ virtual void reset () OVERRIDE; + // ------------------------------------------------------------------------ virtual void newLap (int lap) OVERRIDE {} + }; #endif diff --git a/src/karts/controller/battle_ai.cpp b/src/karts/controller/battle_ai.cpp index c2278e9c4..2aba6e350 100644 --- a/src/karts/controller/battle_ai.cpp +++ b/src/karts/controller/battle_ai.cpp @@ -56,7 +56,6 @@ BattleAI::BattleAI(AbstractKart *kart) } // BattleAI //----------------------------------------------------------------------------- - BattleAI::~BattleAI() { #ifdef AI_DEBUG @@ -66,7 +65,12 @@ BattleAI::~BattleAI() } // ~BattleAI //----------------------------------------------------------------------------- -void BattleAI::findClosestKart(bool use_difficulty, bool find_sta) +/** Find the closest kart around this AI, if consider_difficulty is true, AI + * will try to follow human players more or less depends on difficulty. + * \param consider_difficulty If take current difficulty into account. + * \param find_sta If find \ref SpareTireAI only. + */ +void BattleAI::findClosestKart(bool consider_difficulty, bool find_sta) { float distance = 99999.9f; int closest_kart_num = 0; @@ -88,7 +92,8 @@ void BattleAI::findClosestKart(bool use_difficulty, bool find_sta) // Test whether takes current difficulty into account for closest kart // Notice: it don't affect aiming, this function will be called once // more when use items, which ignore difficulty. - if (m_cur_difficulty == RaceManager::DIFFICULTY_EASY && use_difficulty) + if (m_cur_difficulty == RaceManager::DIFFICULTY_EASY && + consider_difficulty) { // Skip human players for novice mode unless only they are left const AbstractKart* temp = m_world->getKart(start_id); @@ -98,7 +103,7 @@ void BattleAI::findClosestKart(bool use_difficulty, bool find_sta) continue; } else if (m_cur_difficulty == RaceManager::DIFFICULTY_BEST && - use_difficulty) + consider_difficulty) { // Skip AI players for supertux mode const AbstractKart* temp = m_world->getKart(start_id); @@ -122,33 +127,44 @@ void BattleAI::findClosestKart(bool use_difficulty, bool find_sta) } // findClosestKart //----------------------------------------------------------------------------- +/** Find a suitable target to follow, it will find the closest kart first, it's + * used as fallback if no item is found. It takes the current difficulty into + * account, also collect life from \ref SpareTireAI depends on current + * difficulty if actually they are spawned: + * \li Novice and intermediate - collect them only AI has 1 life only. + * \li Expert and supertux - collect them if AI dones't have 3 lives. + */ void BattleAI::findTarget() { - // Find the closest kart first, it's used as fallback if no item is found. - // It takes the current difficulty into account, also collect life from - // spare tire karts when neccessary + bool find_sta = false; + if (m_world->spareTireKartsSpawned()) + { + switch (m_cur_difficulty) + { + case RaceManager::DIFFICULTY_EASY: + case RaceManager::DIFFICULTY_MEDIUM: + { + find_sta = m_world->getKartLife(m_kart->getWorldKartId()) == 1; + break; + } + case RaceManager::DIFFICULTY_HARD: + case RaceManager::DIFFICULTY_BEST: + { + find_sta = m_world->getKartLife(m_kart->getWorldKartId()) != 3; + break; + } + default: assert(false); + } + } - // Collect life depends on current difficulty: - // Novice and intermediate - collect them only AI has 1 life only - // Expert and supertux - collect them if AI dones't have 3 lives - // Also when actually spare tire karts are spawned - bool find_sta = m_world->spareTireKartsSpawned() ? - ((m_cur_difficulty == RaceManager::DIFFICULTY_EASY || - m_cur_difficulty == RaceManager::DIFFICULTY_MEDIUM) && - m_world->getKartLife(m_kart->getWorldKartId()) == 1 ? - true : - (m_cur_difficulty == RaceManager::DIFFICULTY_HARD || - m_cur_difficulty == RaceManager::DIFFICULTY_BEST) && - m_world->getKartLife(m_kart->getWorldKartId()) != 3 ? - true : false) : false; - - findClosestKart(find_sta ? false : true/*use_difficulty*/, find_sta); + bool consider_difficulty = !find_sta; + findClosestKart(consider_difficulty, find_sta); // Find a suitable target to drive to, either powerup or kart if (m_kart->getPowerup()->getType() == PowerupManager::POWERUP_NOTHING && m_kart->getAttachment()->getType() != Attachment::ATTACH_SWATTER && !find_sta) - collectItemInArena(&m_target_point , &m_target_node); + tryCollectItem(&m_target_point , &m_target_node); else { m_target_point = m_closest_kart_point; diff --git a/src/karts/controller/battle_ai.hpp b/src/karts/controller/battle_ai.hpp index 915f34d15..90ec63463 100644 --- a/src/karts/controller/battle_ai.hpp +++ b/src/karts/controller/battle_ai.hpp @@ -33,16 +33,28 @@ class BattleAI : public ArenaAI protected: /** Keep a pointer to world. */ ThreeStrikesBattle *m_world; - virtual void findClosestKart(bool use_difficulty, bool find_sta) OVERRIDE; + + // ------------------------------------------------------------------------ + virtual void findClosestKart(bool consider_difficulty, + bool find_sta) OVERRIDE; + // ------------------------------------------------------------------------ virtual int getCurrentNode() const OVERRIDE; + private: + // ------------------------------------------------------------------------ virtual void findTarget() OVERRIDE; + // ------------------------------------------------------------------------ virtual float getKartDistance(const AbstractKart* kart) const OVERRIDE; + // ------------------------------------------------------------------------ virtual bool isKartOnRoad() const OVERRIDE; + // ------------------------------------------------------------------------ virtual bool isWaiting() const OVERRIDE; + public: BattleAI(AbstractKart *kart); + // ------------------------------------------------------------------------ ~BattleAI(); + }; #endif diff --git a/src/karts/controller/soccer_ai.cpp b/src/karts/controller/soccer_ai.cpp index af4e6925a..c8ed2d3f7 100644 --- a/src/karts/controller/soccer_ai.cpp +++ b/src/karts/controller/soccer_ai.cpp @@ -129,7 +129,7 @@ void SoccerAI::update(float dt) } // update //----------------------------------------------------------------------------- -void SoccerAI::findClosestKart(bool use_difficulty, bool find_sta) +void SoccerAI::findClosestKart(bool consider_difficulty, bool find_sta) { float distance = 99999.9f; const unsigned int n = m_world->getNumKarts(); @@ -165,7 +165,7 @@ void SoccerAI::findClosestKart(bool use_difficulty, bool find_sta) //----------------------------------------------------------------------------- void SoccerAI::findTarget() { - findClosestKart(true/*use_difficulty*/, false/*find_sta*/); + findClosestKart(true/*consider_difficulty*/, false/*find_sta*/); // Check if this AI kart is the one who will chase the ball if (m_world->getBallChaser(m_cur_team) == (signed)m_kart->getWorldKartId()) { @@ -181,7 +181,7 @@ void SoccerAI::findTarget() if (m_kart->getPowerup()->getType() == PowerupManager::POWERUP_NOTHING && m_kart->getAttachment()->getType() != Attachment::ATTACH_SWATTER) { - collectItemInArena(&m_target_point , &m_target_node); + tryCollectItem(&m_target_point , &m_target_node); } else if (m_world->getAttacker(m_cur_team) == (signed)m_kart ->getWorldKartId()) diff --git a/src/karts/controller/soccer_ai.hpp b/src/karts/controller/soccer_ai.hpp index a93832781..f47937f98 100644 --- a/src/karts/controller/soccer_ai.hpp +++ b/src/karts/controller/soccer_ai.hpp @@ -65,7 +65,7 @@ private: virtual bool canSkid(float steer_fraction) OVERRIDE { return m_mini_skid && !(m_overtake_ball || m_chasing_ball); } - virtual void findClosestKart(bool use_difficulty, bool find_sta) OVERRIDE; + virtual void findClosestKart(bool consider_difficulty, bool find_sta) OVERRIDE; virtual void findTarget() OVERRIDE; virtual bool forceBraking() OVERRIDE { return m_force_brake; } virtual int getCurrentNode() const OVERRIDE; From 7505630cd425b1293e9f12674182891cb306605b Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 21 Oct 2016 01:38:30 +0800 Subject: [PATCH 335/350] Add doxygen for Soccer and SpareTire AI --- src/karts/controller/battle_ai.cpp | 32 +++++++++---------- src/karts/controller/soccer_ai.cpp | 40 +++++++++++++++++++++++- src/karts/controller/soccer_ai.hpp | 43 +++++++++++++++++++++----- src/karts/controller/spare_tire_ai.cpp | 22 ++++++++++++- src/karts/controller/spare_tire_ai.hpp | 18 ++++++++++- 5 files changed, 128 insertions(+), 27 deletions(-) diff --git a/src/karts/controller/battle_ai.cpp b/src/karts/controller/battle_ai.cpp index 2aba6e350..6349bcffa 100644 --- a/src/karts/controller/battle_ai.cpp +++ b/src/karts/controller/battle_ai.cpp @@ -139,22 +139,22 @@ void BattleAI::findTarget() bool find_sta = false; if (m_world->spareTireKartsSpawned()) { - switch (m_cur_difficulty) - { - case RaceManager::DIFFICULTY_EASY: - case RaceManager::DIFFICULTY_MEDIUM: - { - find_sta = m_world->getKartLife(m_kart->getWorldKartId()) == 1; - break; - } - case RaceManager::DIFFICULTY_HARD: - case RaceManager::DIFFICULTY_BEST: - { - find_sta = m_world->getKartLife(m_kart->getWorldKartId()) != 3; - break; - } - default: assert(false); - } + switch (m_cur_difficulty) + { + case RaceManager::DIFFICULTY_EASY: + case RaceManager::DIFFICULTY_MEDIUM: + { + find_sta = m_world->getKartLife(m_kart->getWorldKartId()) == 1; + break; + } + case RaceManager::DIFFICULTY_HARD: + case RaceManager::DIFFICULTY_BEST: + { + find_sta = m_world->getKartLife(m_kart->getWorldKartId()) != 3; + break; + } + default: assert(false); + } } bool consider_difficulty = !find_sta; diff --git a/src/karts/controller/soccer_ai.cpp b/src/karts/controller/soccer_ai.cpp index c8ed2d3f7..12e2e03d1 100644 --- a/src/karts/controller/soccer_ai.cpp +++ b/src/karts/controller/soccer_ai.cpp @@ -71,7 +71,6 @@ SoccerAI::SoccerAI(AbstractKart *kart) } // SoccerAI //----------------------------------------------------------------------------- - SoccerAI::~SoccerAI() { #ifdef AI_DEBUG @@ -103,6 +102,10 @@ void SoccerAI::reset() } // reset //----------------------------------------------------------------------------- +/** Update \ref m_front_transform for ball aiming functions, also make AI stop + * after goal. + * \param dt Time step size. + */ void SoccerAI::update(float dt) { #ifdef BALL_AIM_DEBUG @@ -129,6 +132,11 @@ void SoccerAI::update(float dt) } // update //----------------------------------------------------------------------------- +/** Find the closest kart around this AI, it won't find the kart with same + * team, consider_difficulty and find_sta are not used here. + * \param consider_difficulty If take current difficulty into account. + * \param find_sta If find \ref SpareTireAI only. + */ void SoccerAI::findClosestKart(bool consider_difficulty, bool find_sta) { float distance = 99999.9f; @@ -163,6 +171,13 @@ void SoccerAI::findClosestKart(bool consider_difficulty, bool find_sta) } // findClosestKart //----------------------------------------------------------------------------- +/** Find a suitable target to follow, it will first call + * \ref SoccerWorld::getBallChaser to check if this AI should go chasing the + * ball and try to score, otherwise it will call \ref tryCollectItem if + * needed. After that it will call \ref SoccerWorld::getAttacker to see if + * this AI should attack the kart in opposite team which is chasing the ball, + * if not go for the closest kart found by \ref findClosestKart. + */ void SoccerAI::findTarget() { findClosestKart(true/*consider_difficulty*/, false/*find_sta*/); @@ -201,6 +216,11 @@ void SoccerAI::findTarget() } // findTarget //----------------------------------------------------------------------------- +/** Determine the point for aiming when try to steer or overtake the ball. + * AI will overtake the ball if the aiming position calculated by world is + * non-reachable. + * \return The coordinates to aim at. + */ Vec3 SoccerAI::determineBallAimingPosition() { #ifdef BALL_AIM_DEBUG @@ -276,6 +296,11 @@ Vec3 SoccerAI::determineBallAimingPosition() } // determineBallAimingPosition //----------------------------------------------------------------------------- +/** Used in \ref determineBallAimingPosition to test if AI can overtake the + * ball by testing distance. + * \param ball_lc Local coordinates of the ball. + * \return False if the kart is too close to the ball which can't overtake + */ bool SoccerAI::isOvertakable(const Vec3& ball_lc) { // No overtake if ball is behind @@ -298,6 +323,13 @@ bool SoccerAI::isOvertakable(const Vec3& ball_lc) } // isOvertakable //----------------------------------------------------------------------------- +/** Used in \ref determineBallAimingPosition to pick a correct point to + * overtake the ball + * \param ball_lc Local coordinates of the ball. + * \param aim_lc Local coordinates of the aiming position. + * \param[out] overtake_lc Local coordinates of the overtaking position. + * \return True if overtaking is possible. + */ bool SoccerAI::determineOvertakePosition(const Vec3& ball_lc, const Vec3& aim_lc, Vec3* overtake_lc) @@ -434,6 +466,12 @@ bool SoccerAI::determineOvertakePosition(const Vec3& ball_lc, } // determineOvertakePosition //----------------------------------------------------------------------------- +/** Used in \ref determineOvertakePosition to adjust the overtake position + * which is calculated by slope of line if it's too close. + * \param old_slope Old slope calculated. + * \param rotate_up If adjust the slope upwards. + * \return A newly calculated slope. + */ float SoccerAI::rotateSlope(float old_slope, bool rotate_up) { const float theta = atan(old_slope) + (old_slope < 0 ? M_PI : 0); diff --git a/src/karts/controller/soccer_ai.hpp b/src/karts/controller/soccer_ai.hpp index f47937f98..36d21c4f2 100644 --- a/src/karts/controller/soccer_ai.hpp +++ b/src/karts/controller/soccer_ai.hpp @@ -45,41 +45,68 @@ private: /** Keep a pointer to world. */ SoccerWorld *m_world; + /** Save the team this AI belongs to. */ SoccerTeam m_cur_team; + + /** Save the opposite team of this AI team. */ SoccerTeam m_opp_team; /** Define which way to handle to ball, either steer with it, - * or overtake it (Denfense). - */ + * or overtake it (Defense). */ bool m_overtake_ball; + + /** True if \ref forceBraking() is needed to be called. */ bool m_force_brake; + + /** True if AI should steer with the ball. */ bool m_chasing_ball; + /** The front point of kart with the same rotation of center mass, used + * to determine point for aiming with ball */ btTransform m_front_transform; + // ------------------------------------------------------------------------ Vec3 determineBallAimingPosition(); + // ------------------------------------------------------------------------ bool determineOvertakePosition(const Vec3& ball_lc, const Vec3& aim_lc, Vec3* overtake_lc); + // ------------------------------------------------------------------------ bool isOvertakable(const Vec3& ball_lc); + // ------------------------------------------------------------------------ float rotateSlope(float old_slope, bool rotate_up); - + // ------------------------------------------------------------------------ virtual bool canSkid(float steer_fraction) OVERRIDE - { return m_mini_skid && !(m_overtake_ball || m_chasing_ball); } - virtual void findClosestKart(bool consider_difficulty, bool find_sta) OVERRIDE; + { return m_mini_skid && !(m_overtake_ball || m_chasing_ball); } + // ------------------------------------------------------------------------ + virtual void findClosestKart(bool consider_difficulty, + bool find_sta) OVERRIDE; + // ------------------------------------------------------------------------ virtual void findTarget() OVERRIDE; - virtual bool forceBraking() OVERRIDE { return m_force_brake; } + // ------------------------------------------------------------------------ + virtual bool forceBraking() OVERRIDE { return m_force_brake; } + // ------------------------------------------------------------------------ virtual int getCurrentNode() const OVERRIDE; + // ------------------------------------------------------------------------ virtual float getKartDistance(const AbstractKart* kart) const OVERRIDE; + // ------------------------------------------------------------------------ virtual bool ignorePathFinding() OVERRIDE - { return m_overtake_ball || m_chasing_ball; } + { return m_overtake_ball || m_chasing_ball; } + // ------------------------------------------------------------------------ virtual bool isKartOnRoad() const OVERRIDE; + // ------------------------------------------------------------------------ virtual bool isWaiting() const OVERRIDE; - virtual void resetAfterStop() OVERRIDE { m_overtake_ball = false; } + // ------------------------------------------------------------------------ + virtual void resetAfterStop() OVERRIDE { m_overtake_ball = false; } + public: SoccerAI(AbstractKart *kart); + // ------------------------------------------------------------------------ ~SoccerAI(); + // ------------------------------------------------------------------------ virtual void update (float delta) OVERRIDE; + // ------------------------------------------------------------------------ virtual void reset () OVERRIDE; + }; #endif diff --git a/src/karts/controller/spare_tire_ai.cpp b/src/karts/controller/spare_tire_ai.cpp index 18a9f5123..b68faa386 100644 --- a/src/karts/controller/spare_tire_ai.cpp +++ b/src/karts/controller/spare_tire_ai.cpp @@ -61,6 +61,10 @@ void SpareTireAI::reset() } // reset //----------------------------------------------------------------------------- +/** Besides calling update from parent class, it will auto \ref unspawn if + * \ref m_timer reaches zero which it will be decreased here. + * \param dt Time step size. + */ void SpareTireAI::update(float dt) { BattleAI::update(dt); @@ -71,9 +75,11 @@ void SpareTireAI::update(float dt) } // update //----------------------------------------------------------------------------- +/** Randomly find a start node for spare tire kart to move, called after \ref + * spawn. + */ void SpareTireAI::findDefaultPath() { - // Randomly find a start node for spare tire kart to move assert(m_idx == -1); RandomGenerator random; @@ -83,6 +89,9 @@ void SpareTireAI::findDefaultPath() } // findDefaultPath //----------------------------------------------------------------------------- +/** For SpareTireAI, it will pick next node in \ref m_fixed_target_nodes after + * reach the one in \ref m_idx, or the first one if it's the last. + */ void SpareTireAI::findTarget() { assert(m_idx != -1 && m_idx < 4); @@ -95,6 +104,10 @@ void SpareTireAI::findTarget() } // findTarget //----------------------------------------------------------------------------- +/** Spawn the SpareTireAI, it will start appearing in the battle mode and + * moving around. + * \param time_to_last Time before calling \ref unspawn. + */ void SpareTireAI::spawn(float time_to_last) { findDefaultPath(); @@ -108,6 +121,9 @@ void SpareTireAI::spawn(float time_to_last) } // spawn //----------------------------------------------------------------------------- +/** Unspawn the SpareTireAI, it will be hidden in the battle mode but not + * deleted, so it can be called with \ref spawn again later. + */ void SpareTireAI::unspawn() { m_idx = -1; @@ -115,6 +131,10 @@ void SpareTireAI::unspawn() } // unspawn //----------------------------------------------------------------------------- +/** Callback function when a kart crashes into the SpareTireAI, the kart will + * increase one life if its life is not equal 3. A message will be shown too. + * \param k \ref AbstractKart this SpareTireAI crashed. + */ void SpareTireAI::crashed(const AbstractKart *k) { // Nothing happen when two spare tire karts crash each other diff --git a/src/karts/controller/spare_tire_ai.hpp b/src/karts/controller/spare_tire_ai.hpp index 8aa7c7924..39269128d 100644 --- a/src/karts/controller/spare_tire_ai.hpp +++ b/src/karts/controller/spare_tire_ai.hpp @@ -27,22 +27,38 @@ class SpareTireAI : public BattleAI { private: + /** The 4 bounding boxes \ref ArenaNode to follow. */ int m_fixed_target_nodes[4]; + /** The current index of \ref ArenaNode in \ref m_fixed_target_nodes to + * follow, if it's -1, \ref update is not needed to be called. */ int m_idx; + /** Store the time before calling \ref unspawn. */ float m_timer; + // ------------------------------------------------------------------------ virtual void findTarget() OVERRIDE; + // ------------------------------------------------------------------------ void findDefaultPath(); + public: SpareTireAI(AbstractKart *kart); + // ------------------------------------------------------------------------ virtual void crashed(const AbstractKart *k) OVERRIDE; + // ------------------------------------------------------------------------ virtual void update(float delta) OVERRIDE; + // ------------------------------------------------------------------------ virtual void reset() OVERRIDE; + // ------------------------------------------------------------------------ void spawn(float time_to_last); + // ------------------------------------------------------------------------ void unspawn(); - bool isMoving() const { return m_idx != -1; } + // ------------------------------------------------------------------------ + /** Return true if this AI needed to be called \ref update by \ref World, + * ie it is spawned. */ + bool isMoving() const { return m_idx != -1; } + }; #endif From 3e70fa27d9c4f90d29afde72a7443c38d719a064 Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 22 Oct 2016 00:01:48 +0800 Subject: [PATCH 336/350] Add doxygen for Font modules Plus some coding-style cleanup --- src/font/bold_face.cpp | 14 +- src/font/bold_face.hpp | 12 +- src/font/digit_face.cpp | 5 +- src/font/digit_face.hpp | 5 +- src/font/face_ttf.cpp | 14 +- src/font/face_ttf.hpp | 19 ++- src/font/font_manager.cpp | 24 ++-- src/font/font_manager.hpp | 35 ++++- src/font/font_settings.hpp | 30 ++++- src/font/font_with_face.cpp | 107 ++++++++++++---- src/font/font_with_face.hpp | 121 +++++++++++++++--- src/font/regular_face.cpp | 5 +- src/font/regular_face.hpp | 7 +- src/guiengine/engine.cpp | 2 + src/guiengine/scalable_font.cpp | 1 + .../widgets/dynamic_ribbon_widget.cpp | 1 + src/karts/kart.cpp | 1 + src/main.cpp | 2 + src/scriptengine/script_track.cpp | 1 + src/states_screens/options_screen_ui.cpp | 1 + src/utils/debug.cpp | 1 + 21 files changed, 322 insertions(+), 86 deletions(-) diff --git a/src/font/bold_face.cpp b/src/font/bold_face.cpp index e9192de14..4f7ae33df 100644 --- a/src/font/bold_face.cpp +++ b/src/font/bold_face.cpp @@ -18,9 +18,10 @@ #include "font/bold_face.hpp" -#include "font/face_ttf.hpp" - // ---------------------------------------------------------------------------- +/** Constructor of BoldFace. + * \param ttf \ref FaceTTF for BoldFace to use. + */ BoldFace::BoldFace(FaceTTF* ttf) : FontWithFace("BoldFace", ttf) { } // BoldFace @@ -53,3 +54,12 @@ void BoldFace::reset() insertCharacters(preload_chars.c_str()); updateCharactersList(); } // reset + +// ---------------------------------------------------------------------------- +/** Embolden the glyph to make bold font using FT_Outline_Embolden. + * \return A FT_Error value. + */ +int BoldFace::shapeOutline(FT_Outline* outline) const +{ + return FT_Outline_Embolden(outline, getDPI() * 2); +} // shapeOutline diff --git a/src/font/bold_face.hpp b/src/font/bold_face.hpp index 53ddce8f5..e9455395b 100644 --- a/src/font/bold_face.hpp +++ b/src/font/bold_face.hpp @@ -23,24 +23,28 @@ class FaceTTF; +/** A font which uses regular TTFs to render title or important message in STK + * with a bold outline, it shares the same \ref FaceTTF with \ref RegularFace. + * \ingroup font + */ class BoldFace : public FontWithFace { private: - virtual bool supportLazyLoadChar() const OVERRIDE { return true; } - // ------------------------------------------------------------------------ virtual unsigned int getGlyphPageSize() const OVERRIDE { return 1024; } // ------------------------------------------------------------------------ virtual float getScalingFactorOne() const OVERRIDE { return 0.2f; } // ------------------------------------------------------------------------ virtual unsigned int getScalingFactorTwo() const OVERRIDE { return 120; } + // ------------------------------------------------------------------------ + virtual bool isBold() const { return true; } + // ------------------------------------------------------------------------ + virtual int shapeOutline(FT_Outline* outline) const OVERRIDE; public: LEAK_CHECK() // ------------------------------------------------------------------------ BoldFace(FaceTTF* ttf); // ------------------------------------------------------------------------ - virtual ~BoldFace() {} - // ------------------------------------------------------------------------ virtual void init() OVERRIDE; // ------------------------------------------------------------------------ virtual void reset() OVERRIDE; diff --git a/src/font/digit_face.cpp b/src/font/digit_face.cpp index 2403c4d6d..24ca94ef0 100644 --- a/src/font/digit_face.cpp +++ b/src/font/digit_face.cpp @@ -18,9 +18,10 @@ #include "font/digit_face.hpp" -#include "font/face_ttf.hpp" - // ---------------------------------------------------------------------------- +/** Constructor of DigitFace. + * \param ttf \ref FaceTTF for DigitFace to use. + */ DigitFace::DigitFace(FaceTTF* ttf) : FontWithFace("DigitFace", ttf) { } // DigitFace diff --git a/src/font/digit_face.hpp b/src/font/digit_face.hpp index 35bbdb248..a5aed4dab 100644 --- a/src/font/digit_face.hpp +++ b/src/font/digit_face.hpp @@ -23,6 +23,9 @@ class FaceTTF; +/** A font which uses a more cartonish style TTF to render big numbers in STK. + * \ingroup font + */ class DigitFace : public FontWithFace { private: @@ -39,8 +42,6 @@ public: // ------------------------------------------------------------------------ DigitFace(FaceTTF* ttf); // ------------------------------------------------------------------------ - virtual ~DigitFace() {} - // ------------------------------------------------------------------------ virtual void init() OVERRIDE; // ------------------------------------------------------------------------ virtual void reset() OVERRIDE; diff --git a/src/font/face_ttf.cpp b/src/font/face_ttf.cpp index fdb64d572..fb496f590 100644 --- a/src/font/face_ttf.cpp +++ b/src/font/face_ttf.cpp @@ -18,22 +18,29 @@ #include "font/face_ttf.hpp" +#include "font/font_manager.hpp" #include "io/file_manager.hpp" // ---------------------------------------------------------------------------- +/** Constructor. Load all TTFs from a list. + * \param ttf_list List of TTFs to be loaded. + */ FaceTTF::FaceTTF(const std::vector& ttf_list) { for (const std::string& font : ttf_list) { FT_Face face = NULL; + const std::string loc = file_manager + ->getAssetChecked(FileManager::TTF, font.c_str(), true); font_manager->checkFTError(FT_New_Face(font_manager->getFTLibrary(), - (file_manager->getAssetChecked(FileManager::TTF, - font.c_str(), true)).c_str(), 0, &face), "loading fonts"); + loc.c_str(), 0, &face), loc + " is loaded"); m_faces.push_back(face); } } // FaceTTF // ---------------------------------------------------------------------------- +/** Destructor. Clears all TTFs. + */ FaceTTF::~FaceTTF() { for (unsigned int i = 0; i < m_faces.size(); i++) @@ -42,6 +49,9 @@ FaceTTF::~FaceTTF() } } // ~FaceTTF // ---------------------------------------------------------------------------- +/** Return a TTF in \ref m_faces. + * \param i index of TTF file in \ref m_faces. + */ FT_Face FaceTTF::getFace(unsigned int i) const { assert(i < m_faces.size()); diff --git a/src/font/face_ttf.hpp b/src/font/face_ttf.hpp index 90663dab5..6fb1e8c44 100644 --- a/src/font/face_ttf.hpp +++ b/src/font/face_ttf.hpp @@ -19,15 +19,29 @@ #ifndef HEADER_FACE_TTF_HPP #define HEADER_FACE_TTF_HPP -#include "font/font_manager.hpp" +#include "utils/leak_check.hpp" +#include "utils/no_copy.hpp" +#include + +#include +#include FT_FREETYPE_H + +/** This class will load a list of TTF files from \ref STKConfig, and save + * them inside \ref m_faces for \ref FontWithFace to load glyph. + * Each FaceTTF can be used more than once in each instantiation of \ref + * FontWithFace, so it can render characters differently using the same TTF + * file to save memory, for example different outline size. + * \ingroup font + */ class FaceTTF : public NoCopy { private: + /** Contains all TTF files loaded. */ std::vector m_faces; public: - LEAK_CHECK(); + LEAK_CHECK() // ------------------------------------------------------------------------ FaceTTF(const std::vector& ttf_list); // ------------------------------------------------------------------------ @@ -35,6 +49,7 @@ public: // ------------------------------------------------------------------------ FT_Face getFace(unsigned int i) const; // ------------------------------------------------------------------------ + /** Return the total TTF files loaded. */ unsigned int getTotalFaces() const { return m_faces.size(); } }; // FaceTTF diff --git a/src/font/font_manager.cpp b/src/font/font_manager.cpp index 82de84b3c..52a893de3 100644 --- a/src/font/font_manager.cpp +++ b/src/font/font_manager.cpp @@ -28,15 +28,22 @@ FontManager *font_manager = NULL; // ---------------------------------------------------------------------------- +/** Constructor. It will initialize the \ref m_ft_library. + */ FontManager::FontManager() { checkFTError(FT_Init_FreeType(&m_ft_library), "loading freetype library"); } // FontManager // ---------------------------------------------------------------------------- +/** Destructor. Clears all fonts and related stuff. + */ FontManager::~FontManager() { - m_fonts.clearAndDeleteAll(); + for (unsigned int i = 0; i < m_fonts.size(); i++) + delete m_fonts[i]; + m_fonts.clear(); + delete m_normal_ttf; m_normal_ttf = NULL; delete m_digit_ttf; @@ -47,6 +54,8 @@ FontManager::~FontManager() } // ~FontManager // ---------------------------------------------------------------------------- +/** Initialize all \ref FaceTTF and \ref FontWithFace members. + */ void FontManager::loadFonts() { // First load the TTF files required by each font @@ -72,15 +81,10 @@ void FontManager::loadFonts() } // loadFonts // ---------------------------------------------------------------------------- -void FontManager::checkFTError(FT_Error err, const std::string& desc) const -{ - if (err > 0) - { - Log::error("FontManager", "Something wrong when %s!", desc.c_str()); - } -} // checkFTError - -// ---------------------------------------------------------------------------- +/** Unit testing that will try to load all translations in STK, and discover if + * there is any characters required by it are not supported in \ref + * m_normal_ttf. + */ void FontManager::unitTesting() { std::vector list = *(translations->getLanguageList()); diff --git a/src/font/font_manager.hpp b/src/font/font_manager.hpp index f46249944..156cd771c 100644 --- a/src/font/font_manager.hpp +++ b/src/font/font_manager.hpp @@ -19,14 +19,18 @@ #ifndef HEADER_FONT_MANAGER_HPP #define HEADER_FONT_MANAGER_HPP +/** \defgroup font Font + * This module stores font files and tools used to draw characters in STK. + */ + #include "utils/leak_check.hpp" #include "utils/log.hpp" #include "utils/no_copy.hpp" -#include "utils/ptr_vector.hpp" #include #include #include +#include #include #include FT_FREETYPE_H @@ -34,17 +38,26 @@ class FaceTTF; class FontWithFace; +/** This class stores all font files required in STK. + * \ingroup font + */ class FontManager : public NoCopy { private: - PtrVector m_fonts; + /** Stores all \ref FontWithFace used in STK. */ + std::vector m_fonts; + /** A FreeType library, it holds the FT_Face internally inside freetype. */ FT_Library m_ft_library; + /** TTF files used in \ref BoldFace and \ref RegularFace. */ FaceTTF* m_normal_ttf; + /** TTF files used in \ref DigitFace. */ FaceTTF* m_digit_ttf; + /** Map type for each \ref FontWithFace with a index, save getting time in + * \ref getFont. */ std::unordered_map m_font_type_map; public: @@ -54,11 +67,12 @@ public: // ------------------------------------------------------------------------ ~FontManager(); // ------------------------------------------------------------------------ + /** Return a specfic type of \ref FontWithFace found in \ref m_fonts. */ template T* getFont() { T* out = NULL; const unsigned int n = m_font_type_map[std::type_index(typeid(T))]; - out = dynamic_cast(m_fonts.get(n)); + out = dynamic_cast(m_fonts[n]); if (out != NULL) { return out; @@ -68,16 +82,23 @@ public: } // ------------------------------------------------------------------------ /** Check for any error discovered in a freetype function that will return - * a FT_Error value. + * a FT_Error value, and log into the terminal. * \param err The Freetype function. - * \param desc The description of what is the function doing. - */ - void checkFTError(FT_Error err, const std::string& desc) const; + * \param desc The description of what is the function doing. */ + void checkFTError(FT_Error err, const std::string& desc) const + { + if (err > 0) + { + Log::error("FontManager", "Something wrong when %s! The error " + "code was %d.", desc.c_str(), err); + } + } // ------------------------------------------------------------------------ void loadFonts(); // ------------------------------------------------------------------------ void unitTesting(); // ------------------------------------------------------------------------ + /** Return the \ref m_ft_library. */ FT_Library getFTLibrary() const { return m_ft_library; } }; // FontManager diff --git a/src/font/font_settings.hpp b/src/font/font_settings.hpp index a833b8c9f..3dc183584 100644 --- a/src/font/font_settings.hpp +++ b/src/font/font_settings.hpp @@ -25,21 +25,34 @@ using namespace irr; +/** This class stores settings when rendering fonts, used when instantiating + * \ref irr::gui::ScalableFont. + * \ingroup font + */ class FontSettings { private: + /** True if black border will be drawn when rendering. */ bool m_black_border; + /** If true, characters will have right alignment when rendering, for RTL + * language. */ bool m_rtl; + /** Scaling when rendering. */ float m_scale; + /** True if shadow will be drawn when rendering. */ bool m_shadow; + /** Save the color of shadow when rendering. */ video::SColor m_shadow_color; + public: LEAK_CHECK() // ------------------------------------------------------------------------ + /** Constructor. It will initialize all members with default values if no + * parameter is given. */ FontSettings(bool black_border = false, bool rtl = false, float scale = 1.0f, bool shadow = false, const video::SColor& color = video::SColor(0, 0, 0, 0)) @@ -51,26 +64,39 @@ public: m_shadow_color = color; } // ------------------------------------------------------------------------ - ~FontSettings() {} - // ------------------------------------------------------------------------ + /** Set the scaling. + * \param scale Scaling to be set. */ void setScale(float scale) { m_scale = scale; } // ------------------------------------------------------------------------ + /** Return the scaling. */ float getScale() const { return m_scale; } // ------------------------------------------------------------------------ + /** Set the color of shadow. + * \param col The color of shadow to be set. */ void setShadowColor(const video::SColor &col) { m_shadow_color = col; } // ------------------------------------------------------------------------ + /** Return the color of shadow. */ const video::SColor& getShadowColor() const { return m_shadow_color; } // ------------------------------------------------------------------------ + /** Return if shadow is enabled. */ bool useShadow() const { return m_shadow; } // ------------------------------------------------------------------------ + /** Set whether shadow is enabled. + * \param shadow If it's enabled. */ void setShadow(bool shadow) { m_shadow = shadow; } // ------------------------------------------------------------------------ + /** Set whether black border is enabled. + * \param border If it's enabled. */ void setBlackBorder(bool border) { m_black_border = border; } // ------------------------------------------------------------------------ + /** Return if black border is enabled. */ bool useBlackBorder() const { return m_black_border; } // ------------------------------------------------------------------------ + /** Set right text alignment for RTL language. + * \param rtl If it's enabled. */ void setRTL(bool rtl) { m_rtl = rtl; } // ------------------------------------------------------------------------ + /** Return if right text alignment for RTL language is enabled. */ bool isRTL() const { return m_rtl; } }; // FontSettings diff --git a/src/font/font_with_face.cpp b/src/font/font_with_face.cpp index 7dedffbef..08128fe5d 100644 --- a/src/font/font_with_face.cpp +++ b/src/font/font_with_face.cpp @@ -18,17 +18,20 @@ #include "font/font_with_face.hpp" -#include "font/bold_face.hpp" #include "font/face_ttf.hpp" +#include "font/font_manager.hpp" +#include "font/font_settings.hpp" #include "graphics/2dutils.hpp" #include "graphics/irr_driver.hpp" #include "guiengine/engine.hpp" #include "guiengine/skin.hpp" #include "utils/string_utils.hpp" -#include FT_OUTLINE_H - // ---------------------------------------------------------------------------- +/** Constructor. It will initialize the \ref m_spritebank and TTF files to use. + * \param name The name of face, used by irrlicht to distinguish spritebank. + * \param ttf \ref FaceTTF for this face to use. + */ FontWithFace::FontWithFace(const std::string& name, FaceTTF* ttf) { m_spritebank = irr_driver->getGUI()->addEmptySpriteBank(name.c_str()); @@ -43,6 +46,8 @@ FontWithFace::FontWithFace(const std::string& name, FaceTTF* ttf) } // FontWithFace // ---------------------------------------------------------------------------- +/** Destructor. Clears the glyph page and sprite bank. + */ FontWithFace::~FontWithFace() { m_page->drop(); @@ -56,6 +61,8 @@ FontWithFace::~FontWithFace() } // ~FontWithFace // ---------------------------------------------------------------------------- +/** Initialize the font structure, but don't load glyph here. + */ void FontWithFace::init() { setDPI(); @@ -83,7 +90,11 @@ void FontWithFace::init() reset(); } // init + // ---------------------------------------------------------------------------- +/** Clear all the loaded characters, sub-class can do pre-loading of characters + * after this. + */ void FontWithFace::reset() { m_new_char_holder.clear(); @@ -94,6 +105,12 @@ void FontWithFace::reset() } // reset // ---------------------------------------------------------------------------- +/** Convert a character to a glyph index in one of the font in \ref m_face_ttf, + * it will find the first TTF that supports this character, if the final + * glyph_index is 0, this means such character is not supported by all TTFs in + * \ref m_face_ttf. + * \param c The character to be loaded. + */ void FontWithFace::loadGlyphInfo(wchar_t c) { unsigned int font_number = 0; @@ -108,11 +125,12 @@ void FontWithFace::loadGlyphInfo(wchar_t c) } // loadGlyphInfo // ---------------------------------------------------------------------------- +/** Create a new glyph page by filling it with transparent content. + */ void FontWithFace::createNewGlyphPage() { - // Clean the current glyph page by filling it with transparent content m_page->fill(video::SColor(0, 255, 255, 255)); - m_temp_height = 0; + m_current_height = 0; m_used_width = 0; m_used_height = 0; @@ -140,6 +158,10 @@ void FontWithFace::createNewGlyphPage() } // createNewGlyphPage // ---------------------------------------------------------------------------- +/** Render a glyph for a character into bitmap and save it into the glyph page. + * \param c The character to be loaded. + * \param c \ref GlyphInfo for the character. + */ void FontWithFace::insertGlyph(wchar_t c, const GlyphInfo& gi) { assert(gi.glyph_index > 0); @@ -147,7 +169,7 @@ void FontWithFace::insertGlyph(wchar_t c, const GlyphInfo& gi) FT_Face cur_face = m_face_ttf->getFace(gi.font_number); FT_GlyphSlot slot = cur_face->glyph; - // Faces may be shared across regular and bold, + // Same face may be shared across the different FontWithFace, // so reset dpi each time font_manager->checkFTError(FT_Set_Pixel_Sizes(cur_face, 0, getDPI()), "setting DPI"); @@ -155,15 +177,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(this) != NULL) - { - // Embolden the outline of the glyph - font_manager->checkFTError(FT_Outline_Embolden(&(slot->outline), - getDPI() * 2), "embolden a glyph"); - } + font_manager->checkFTError(shapeOutline(&(slot->outline)), + "shaping outline"); font_manager->checkFTError(FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL), - "render a glyph to bitmap"); + "rendering a glyph to bitmap"); // Convert to an anti-aliased bitmap FT_Bitmap bits = slot->bitmap; @@ -176,7 +194,7 @@ void FontWithFace::insertGlyph(wchar_t c, const GlyphInfo& gi) true, 0); if ((m_used_width + texture_size.Width > getGlyphPageSize() && - m_used_height + m_temp_height + texture_size.Height > + m_used_height + m_current_height + texture_size.Height > getGlyphPageSize()) || m_used_height + texture_size.Height > getGlyphPageSize()) { @@ -244,8 +262,8 @@ void FontWithFace::insertGlyph(wchar_t c, const GlyphInfo& gi) if (m_used_width + texture_size.Width > getGlyphPageSize()) { m_used_width = 0; - m_used_height += m_temp_height; - m_temp_height = 0; + m_used_height += m_current_height; + m_current_height = 0; } // Copy to the full glyph page @@ -283,11 +301,13 @@ void FontWithFace::insertGlyph(wchar_t c, const GlyphInfo& gi) // Store used area m_used_width += texture_size.Width; - if (m_temp_height < texture_size.Height) - m_temp_height = texture_size.Height; + if (m_current_height < texture_size.Height) + m_current_height = texture_size.Height; } // insertGlyph // ---------------------------------------------------------------------------- +/** Update the supported characters for this font if required. + */ void FontWithFace::updateCharactersList() { if (m_fallback_font != NULL) @@ -321,6 +341,10 @@ void FontWithFace::updateCharactersList() } // updateCharactersList // ---------------------------------------------------------------------------- +/** Write the current glyph page in png inside current running directory. + * Mainly for debug use. + * \param name The file name. + */ void FontWithFace::dumpGlyphPage(const std::string& name) { for (unsigned int i = 0; i < m_spritebank->getTextureCount(); i++) @@ -341,20 +365,23 @@ void FontWithFace::dumpGlyphPage(const std::string& name) } // dumpGlyphPage // ---------------------------------------------------------------------------- +/** Write the current glyph page in png inside current running directory. + * Useful in gdb without parameter. + */ void FontWithFace::dumpGlyphPage() { dumpGlyphPage("face"); } // dumpGlyphPage // ---------------------------------------------------------------------------- +/** Set the face dpi which 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. + * Bold text will range from 0.2, in 640x* resolutions (won't scale below + * that) to 0.4, in 1024x* resolutions, and linearly up. + */ void FontWithFace::setDPI() { - // 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 - // Bold text will range from 0.2, in 640x* resolutions (won't scale - // below that) to 0.4, in 1024x* resolutions, and linearly up const int screen_width = irr_driver->getFrameSize().Width; const int screen_height = irr_driver->getFrameSize().Height; float scale = std::max(0, screen_width - 640) / 564.0f; @@ -371,6 +398,10 @@ void FontWithFace::setDPI() } // setDPI // ---------------------------------------------------------------------------- +/** Return the \ref FontArea about a character. + * \param c The character to get. + * \param[out] fallback_font Whether fallback font is used. + */ const FontWithFace::FontArea& FontWithFace::getAreaFromCharacter(const wchar_t c, bool* fallback_font) const @@ -397,6 +428,12 @@ const FontWithFace::FontArea& } // getAreaFromCharacter // ---------------------------------------------------------------------------- +/** Get the dimension of text with support to different \ref FontSettings, + * it will also do checking for missing characters in font and lazy load them. + * \param text The text to be calculated. + * \param font_settings \ref FontSettings to use. + * \return The dimension of text + */ core::dimension2d FontWithFace::getDimension(const wchar_t* text, FontSettings* font_settings) { @@ -441,6 +478,12 @@ core::dimension2d FontWithFace::getDimension(const wchar_t* text, } // getDimension // ---------------------------------------------------------------------------- +/** Calculate the index of the character in the text on a specific position. + * \param text The text to be calculated. + * \param pixel_x The specific position. + * \param font_settings \ref FontSettings to use. + * \return The index of the character, -1 means no character in such position. + */ int FontWithFace::getCharacterFromPos(const wchar_t* text, int pixel_x, FontSettings* font_settings) const { @@ -466,6 +509,17 @@ int FontWithFace::getCharacterFromPos(const wchar_t* text, int pixel_x, } // getCharacterFromPos // ---------------------------------------------------------------------------- +/** Render text and clip it to the specified rectangle if wanted, it will also + * do checking for missing characters in font and lazy load them. + * \param text The text to be rendering. + * \param position The position to be rendering. + * \param color The color used when rendering. + * \param hcenter If rendered horizontally center. + * \param vcenter If rendered vertically center. + * \param clip If clipping is needed. + * \param font_settings \ref FontSettings to use. + * \param char_collector \ref FontCharCollector to render billboard text. + */ void FontWithFace::render(const core::stringw& text, const core::rect& position, const video::SColor& color, bool hcenter, @@ -473,7 +527,6 @@ void FontWithFace::render(const core::stringw& text, FontSettings* font_settings, FontCharCollector* char_collector) { - const bool is_bold_face = (dynamic_cast(this) != NULL); const bool black_border = font_settings ? font_settings->useBlackBorder() : false; const bool rtl = font_settings ? font_settings->isRTL() : false; @@ -621,7 +674,7 @@ void FontWithFace::render(const core::stringw& text, const int sprite_amount = sprites.size(); - if ((black_border || is_bold_face) && char_collector == NULL) + if ((black_border || isBold()) && char_collector == NULL) { // Draw black border first, to make it behind the real character // which make script language display better @@ -693,7 +746,7 @@ void FontWithFace::render(const core::stringw& text, m_fallback_font->m_spritebank->getTexture(tex_id) : m_spritebank->getTexture(tex_id)); - if (fallback[n] || is_bold_face) + if (fallback[n] || isBold()) { video::SColor top = GUIEngine::getSkin()->getColor("font::top"); video::SColor bottom = GUIEngine::getSkin() diff --git a/src/font/font_with_face.hpp b/src/font/font_with_face.hpp index 52417a03c..5e2ecfbcb 100644 --- a/src/font/font_with_face.hpp +++ b/src/font/font_with_face.hpp @@ -19,47 +19,83 @@ #ifndef HEADER_FONT_WITH_FACE_HPP #define HEADER_FONT_WITH_FACE_HPP -#include "font/font_manager.hpp" -#include "font/font_settings.hpp" #include "utils/cpp2011.hpp" +#include "utils/leak_check.hpp" +#include "utils/no_copy.hpp" #include #include #include +#include +#include FT_FREETYPE_H +#include FT_OUTLINE_H + +#include + +using namespace irr; + const int BEARING = 64; class FaceTTF; +class FontSettings; +/** An abstract class which contains functions which convert vector fonts into + * bitmap and render them in STK. To make STK draw characters with different + * render option (like scaling, shadow) using a same FontWithFace, you need + * to wrap this with \ref irr::gui::ScalableFont and configure the + * \ref FontSettings for it. + * \ingroup font + */ class FontWithFace : public NoCopy { public: + /** A class for \ref STKTextBillboard to get font info to render billboard + * text. */ class FontCharCollector { public: + /** Collect the character info for billboard text. + * \param texture The texture of the character. + * \param destRect The destination rectangle + * \param sourceRect The source rectangle in the glyph page + * \param colors The color to render it. */ virtual void collectChar(video::ITexture* texture, const core::rect& destRect, const core::rect& sourceRect, const video::SColor* const colors) = 0; }; + /** Glyph metrics for each glyph loaded. */ struct FontArea { FontArea() : advance_x(0), bearing_x(0) ,offset_y(0), offset_y_bt(0), spriteno(0) {} + /** Advance width for horizontal layout. */ int advance_x; + /** Left side bearing for horizontal layout. */ int bearing_x; + /** Top side bearing for horizontal layout. */ int offset_y; + /** Top side bearing for horizontal layout used in billboard text. */ int offset_y_bt; + /** Index number in sprite bank. */ int spriteno; }; protected: - int m_font_max_height; + /** Used in vertical dimension calculation. */ + int m_font_max_height; - int m_glyph_max_height; + /** Used in top side bearing calculation. */ + int m_glyph_max_height; // ------------------------------------------------------------------------ + /** Check characters to see if they are loaded in font, if not load them. + * For font that doesn't need lazy loading, nothing will be done. + * \param in_ptr Characters to check. + * \param first_load If true, it will ignore \ref supportLazyLoadChar, + * which is called in \ref reset. */ void insertCharacters(const wchar_t* in_ptr, bool first_load = false) { if (!supportLazyLoadChar() && !first_load) return; @@ -88,44 +124,69 @@ protected: // ------------------------------------------------------------------------ void updateCharactersList(); // ------------------------------------------------------------------------ + /** Set the fallback font for this font, so if some character is missing in + * this font, it will use that fallback font to try rendering it. + * \param face A \ref FontWithFace font. */ void setFallbackFont(FontWithFace* face) { m_fallback_font = face; } // ------------------------------------------------------------------------ + /** Set the scaling of fallback font. + * \param scale The scaling to set. */ void setFallbackFontScale(float scale) { m_fallback_font_scale = scale; } private: + /** Mapping of glyph index to a TTF in \ref FaceTTF. */ struct GlyphInfo { + GlyphInfo(unsigned int font_num = 0, unsigned int glyph_idx = 0) : + font_number(font_num), glyph_index(glyph_idx) {} + /** Index to a TTF in \ref FaceTTF. */ unsigned int font_number; + /** Glyph index in the TTF, 0 means no such glyph. */ unsigned int glyph_index; - GlyphInfo(unsigned int first = 0, unsigned int second = 0) - { - font_number = first; - glyph_index = second; - } }; + /** \ref FaceTTF to load glyph from. */ FaceTTF* m_face_ttf; + /** Fallback font to use if some character isn't supported by this font. */ FontWithFace* m_fallback_font; + + /** Scaling for fallback font. */ float m_fallback_font_scale; - /** A temporary holder stored new char to be inserted. */ + /** A temporary holder to store new characters to be inserted. */ std::set m_new_char_holder; + /** Sprite bank to store each glyph. */ gui::IGUISpriteBank* m_spritebank; /** A full glyph page for this font. */ video::IImage* m_page; - unsigned int m_temp_height; + /** The current max height at current drawing line in glyph page. */ + unsigned int m_current_height; + + /** The used width in glyph page. */ unsigned int m_used_width; + + /** The used height in glyph page. */ unsigned int m_used_height; + + /** The dpi of this font. */ unsigned int m_face_dpi; + /** Store a list of supported character to a \ref FontArea. */ std::map m_character_area_map; + + /** Store a list of loaded and tested character to a \ref GlyphInfo. */ std::map m_character_glyph_info_map; // ------------------------------------------------------------------------ + /** Return a character width. + * \param area \ref FontArea to get glyph metrics. + * \param fallback If fallback font is used. + * \param scale The scaling of the character. + * \return The calculated width with suitable scaling. */ float getCharWidth(const FontArea& area, bool fallback, float scale) const { if (fallback) @@ -134,6 +195,9 @@ private: return area.advance_x * scale; } // ------------------------------------------------------------------------ + /** Test if a character has already been tried to be loaded. + * \param c Character to test. + * \return True if tested. */ bool loadedChar(wchar_t c) const { std::map::const_iterator n = @@ -143,6 +207,10 @@ private: return false; } // ------------------------------------------------------------------------ + /** Get the \ref GlyphInfo from \ref m_character_glyph_info_map about a + * character. + * \param c Character to get. + * \return \ref GlyphInfo of this character. */ const GlyphInfo& getGlyphInfo(wchar_t c) const { std::map::const_iterator n = @@ -152,6 +220,10 @@ private: return n->second; } // ------------------------------------------------------------------------ + /** Tells whether a character is supported by all TTFs in \ref m_face_ttf + * which is determined by \ref GlyphInfo of this character. + * \param c Character to test. + * \return True if it's supported. */ bool supportChar(wchar_t c) { std::map::const_iterator n = @@ -167,22 +239,36 @@ private: // ------------------------------------------------------------------------ void createNewGlyphPage(); // ------------------------------------------------------------------------ + /** Add a character into \ref m_new_char_holder for lazy loading later. */ void addLazyLoadChar(wchar_t c) { m_new_char_holder.insert(c); } // ------------------------------------------------------------------------ void insertGlyph(wchar_t c, const GlyphInfo& gi); // ------------------------------------------------------------------------ void setDPI(); // ------------------------------------------------------------------------ - virtual bool supportLazyLoadChar() const = 0; + /** Override it if sub-class should not do lazy loading characters. */ + virtual bool supportLazyLoadChar() const { return true; } // ------------------------------------------------------------------------ + /** Defined by sub-class about the texture size of glyph page, it should be + * a power of two. */ virtual unsigned int getGlyphPageSize() const = 0; // ------------------------------------------------------------------------ + /** Defined by sub-class about the scaling factor 1. */ virtual float getScalingFactorOne() const = 0; // ------------------------------------------------------------------------ + /** Defined by sub-class about the scaling factor 2. */ virtual unsigned int getScalingFactorTwo() const = 0; + // ------------------------------------------------------------------------ + /** Override it if sub-class has bold outline. */ + virtual bool isBold() const { return false; } + // ------------------------------------------------------------------------ + /** Override it if any outline shaping is needed to be done before + * rendering the glyph into bitmap. + * \return A FT_Error value if needed. */ + virtual int shapeOutline(FT_Outline* outline) const { return 0; } public: - LEAK_CHECK(); + LEAK_CHECK() // ------------------------------------------------------------------------ FontWithFace(const std::string& name, FaceTTF* ttf); // ------------------------------------------------------------------------ @@ -204,22 +290,17 @@ public: FontSettings* font_settings, FontCharCollector* char_collector = NULL); // ------------------------------------------------------------------------ - /** Write the current glyph page in png inside current running directory. - * Mainly for debug use. - * \param name The file name. - */ void dumpGlyphPage(const std::string& name); // ------------------------------------------------------------------------ - /** Write the current glyph page in png inside current running directory. - * Useful in gdb without parameter. - */ void dumpGlyphPage(); // ------------------------------------------------------------------------ + /** Return the sprite bank. */ gui::IGUISpriteBank* getSpriteBank() const { return m_spritebank; } // ------------------------------------------------------------------------ const FontArea& getAreaFromCharacter(const wchar_t c, bool* fallback_font) const; // ------------------------------------------------------------------------ + /** Return the dpi of this face. */ unsigned int getDPI() const { return m_face_dpi; } }; // FontWithFace diff --git a/src/font/regular_face.cpp b/src/font/regular_face.cpp index 0a5fc500a..78d098ce6 100644 --- a/src/font/regular_face.cpp +++ b/src/font/regular_face.cpp @@ -16,11 +16,12 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#include "font/face_ttf.hpp" - #include "font/regular_face.hpp" // ---------------------------------------------------------------------------- +/** Constructor of RegularFace. + * \param ttf \ref FaceTTF for RegularFace to use. + */ RegularFace::RegularFace(FaceTTF* ttf) : FontWithFace("RegularFace", ttf) { } // RegularFace diff --git a/src/font/regular_face.hpp b/src/font/regular_face.hpp index 54ac41d4a..c6bd22d14 100644 --- a/src/font/regular_face.hpp +++ b/src/font/regular_face.hpp @@ -23,11 +23,12 @@ class FaceTTF; +/** A font which uses regular TTFs to most text in STK. + * \ingroup font + */ class RegularFace : public FontWithFace { private: - virtual bool supportLazyLoadChar() const OVERRIDE { return true; } - // ------------------------------------------------------------------------ virtual unsigned int getGlyphPageSize() const OVERRIDE { return 512; } // ------------------------------------------------------------------------ virtual float getScalingFactorOne() const OVERRIDE { return 0.7f; } @@ -39,8 +40,6 @@ public: // ------------------------------------------------------------------------ RegularFace(FaceTTF* ttf); // ------------------------------------------------------------------------ - virtual ~RegularFace() {} - // ------------------------------------------------------------------------ virtual void init() OVERRIDE; // ------------------------------------------------------------------------ virtual void reset() OVERRIDE; diff --git a/src/guiengine/engine.cpp b/src/guiengine/engine.cpp index 8cbc39ae7..92c22a1ee 100644 --- a/src/guiengine/engine.cpp +++ b/src/guiengine/engine.cpp @@ -659,6 +659,8 @@ namespace GUIEngine #include "config/user_config.hpp" #include "font/bold_face.hpp" #include "font/digit_face.hpp" +#include "font/font_manager.hpp" +#include "font/font_settings.hpp" #include "font/regular_face.hpp" #include "input/input_manager.hpp" #include "io/file_manager.hpp" diff --git a/src/guiengine/scalable_font.cpp b/src/guiengine/scalable_font.cpp index dfd035ba9..2b5966506 100644 --- a/src/guiengine/scalable_font.cpp +++ b/src/guiengine/scalable_font.cpp @@ -18,6 +18,7 @@ #include "guiengine/scalable_font.hpp" +#include "font/font_settings.hpp" #include "font/font_with_face.hpp" #include "utils/translation.hpp" diff --git a/src/guiengine/widgets/dynamic_ribbon_widget.cpp b/src/guiengine/widgets/dynamic_ribbon_widget.cpp index d55a6bf33..548a1ab14 100644 --- a/src/guiengine/widgets/dynamic_ribbon_widget.cpp +++ b/src/guiengine/widgets/dynamic_ribbon_widget.cpp @@ -15,6 +15,7 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#include "font/font_manager.hpp" #include "font/regular_face.hpp" #include "guiengine/engine.hpp" #include "guiengine/scalable_font.hpp" diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 64c14f204..bc07b8561 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -26,6 +26,7 @@ #include "config/player_manager.hpp" #include "config/user_config.hpp" #include "font/bold_face.hpp" +#include "font/font_manager.hpp" #include "graphics/camera.hpp" #include "graphics/central_settings.hpp" #include "graphics/explosion.hpp" diff --git a/src/main.cpp b/src/main.cpp index 8ee16f0ad..44d57c84e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -73,6 +73,8 @@ This module handles the user configuration, the supertuxkart configuration file (which contains options usually not edited by the player) and the input configuration file. + \li \ref font : + This module stores font files and tools used to draw characters in STK. \li \ref graphics : This module contains the core graphics engine, that is mostly a thin layer on top of irrlicht providing some additional features we need for STK diff --git a/src/scriptengine/script_track.cpp b/src/scriptengine/script_track.cpp index d8cc4e465..16f00e914 100644 --- a/src/scriptengine/script_track.cpp +++ b/src/scriptengine/script_track.cpp @@ -20,6 +20,7 @@ #include "animations/three_d_animation.hpp" #include "font/digit_face.hpp" +#include "font/font_manager.hpp" #include "graphics/central_settings.hpp" #include "graphics/stk_text_billboard.hpp" #include "guiengine/scalable_font.hpp" diff --git a/src/states_screens/options_screen_ui.cpp b/src/states_screens/options_screen_ui.cpp index 4332e896d..3549c581b 100644 --- a/src/states_screens/options_screen_ui.cpp +++ b/src/states_screens/options_screen_ui.cpp @@ -24,6 +24,7 @@ #include "config/player_manager.hpp" #include "config/user_config.hpp" #include "font/bold_face.hpp" +#include "font/font_manager.hpp" #include "font/regular_face.hpp" #include "guiengine/scalable_font.hpp" #include "guiengine/screen.hpp" diff --git a/src/utils/debug.cpp b/src/utils/debug.cpp index a83fbb161..a8d4dd035 100644 --- a/src/utils/debug.cpp +++ b/src/utils/debug.cpp @@ -21,6 +21,7 @@ #include "config/user_config.hpp" #include "font/bold_face.hpp" #include "font/digit_face.hpp" +#include "font/font_manager.hpp" #include "font/regular_face.hpp" #include "graphics/camera_debug.hpp" #include "graphics/camera_fps.hpp" From cf557f69823bc31176ab5ced51306f9b0e1aaf55 Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 22 Oct 2016 10:46:30 +0800 Subject: [PATCH 337/350] Fix release build --- src/font/face_ttf.hpp | 1 + src/font/font_with_face.hpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/font/face_ttf.hpp b/src/font/face_ttf.hpp index 6fb1e8c44..93d5e115e 100644 --- a/src/font/face_ttf.hpp +++ b/src/font/face_ttf.hpp @@ -22,6 +22,7 @@ #include "utils/leak_check.hpp" #include "utils/no_copy.hpp" +#include #include #include diff --git a/src/font/font_with_face.hpp b/src/font/font_with_face.hpp index 5e2ecfbcb..0fb4b5f1d 100644 --- a/src/font/font_with_face.hpp +++ b/src/font/font_with_face.hpp @@ -24,6 +24,7 @@ #include "utils/no_copy.hpp" #include +#include #include #include From 68ea2ed3b2ac5f6931a43680c7220a5165af5441 Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 22 Oct 2016 12:33:47 +0800 Subject: [PATCH 338/350] Fix typo --- src/font/regular_face.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/font/regular_face.hpp b/src/font/regular_face.hpp index c6bd22d14..0fa7b9d95 100644 --- a/src/font/regular_face.hpp +++ b/src/font/regular_face.hpp @@ -23,7 +23,7 @@ class FaceTTF; -/** A font which uses regular TTFs to most text in STK. +/** A font which uses regular TTFs to render most text in STK. * \ingroup font */ class RegularFace : public FontWithFace From 6a578e374fc2fe45191111ad0d81a45262b473e3 Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 25 Oct 2016 07:53:52 +1100 Subject: [PATCH 339/350] Fix LAN handling when connecting via a WAN server. --- src/main.cpp | 59 +++++++++++++++---- src/network/network_config.cpp | 26 +++++--- src/network/network_config.hpp | 45 +++++++++----- src/network/protocols/connect_to_peer.cpp | 37 +++++++++++- src/network/protocols/connect_to_peer.hpp | 7 +++ src/network/protocols/connect_to_server.cpp | 2 +- src/network/protocols/get_public_address.cpp | 46 ++++++++++++++- src/network/protocols/get_public_address.hpp | 11 +++- .../protocols/server_lobby_room_protocol.cpp | 2 +- src/network/servers_manager.cpp | 3 +- src/network/stk_host.cpp | 16 +++-- 11 files changed, 207 insertions(+), 47 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 8ee16f0ad..9dd78084d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -31,26 +31,54 @@ * Here is an overview of the high-level interactions between modules : \dot digraph interaction { - race -> modes +# race -> modes race -> tracks race -> karts - modes -> tracks - modes -> karts +# modes -> tracks +# modes -> karts tracks -> graphics karts -> graphics tracks -> items - graphics -> irrlicht - guiengine -> irrlicht - states_screens -> guiengine - states_screens -> input - guiengine -> input - karts->physics - tracks->physics - karts -> controller - input->controller + items -> graphics + animations -> graphics + graphics -> "Antarctica/irrlicht" +# guiengine -> irrlicht +# states_screens -> guiengine +# input -> states_screens + input -> guiengine + guiengine -> font_system + karts -> physics + physics -> karts + tracks -> physics + ai -> controller + controller -> karts + input -> controller tracks -> animations physics -> animations - } + animations -> physics + karts -> audio + physics -> audio + "translations\n(too many connections\nto draw)" + "configuration\n(too many connections\nto draw)" +# addons -> tracks +# addons -> karts + guiengine -> addons + guiengine -> race + addons -> online_manager + challenges -> race +# challenges -> modes + guiengine -> challenges + online_manager -> addons + online_manager -> "STK Server" + "STK Server" -> online_manager + karts -> replay + replay + # force karts and tracks on the same level, looks better this way + subgraph { + rank = same; karts; tracks; + } + +} \enddot Note that this graph is only an approximation because the real one would be @@ -182,6 +210,7 @@ #include "network/rewind_manager.hpp" #include "network/servers_manager.hpp" #include "network/stk_host.hpp" +#include "network/protocols/get_public_address.hpp" #include "online/profile_manager.hpp" #include "online/request_manager.hpp" #include "race/grand_prix_manager.hpp" @@ -546,6 +575,7 @@ void cmdLineHelp() " --login=s Automatically log in (set the login).\n" " --password=s Automatically log in (set the password).\n" " --port=n Port number to use.\n" + " --my-address=1.1.1.1:1 Own IP address (can replace stun protocol)\n" " --max-players=n Maximum number of clients (server only).\n" " --no-console Does not write messages in the console but to\n" " stdout.log.\n" @@ -981,6 +1011,9 @@ int handleCmdLine() if(CommandLine::has("--password", &s)) password = s.c_str(); + if (CommandLine::has("--my-address", &s)) + GetPublicAddress::setMyIPAddress(s); + // Race parameters if(CommandLine::has("--kartsize-debug")) { diff --git a/src/network/network_config.cpp b/src/network/network_config.cpp index 2a2aa0517..549b4b9db 100644 --- a/src/network/network_config.cpp +++ b/src/network/network_config.cpp @@ -30,17 +30,19 @@ NetworkConfig *NetworkConfig::m_network_config = NULL; * instance. */ // ============================================================================ -/** Constructor for a client +/** Constructor. */ NetworkConfig::NetworkConfig() { - m_network_type = NETWORK_NONE; - m_is_server = false; - m_max_players = 4; - m_is_registered = false; - m_server_name = ""; - m_password = ""; - m_private_port = 0; + m_network_type = NETWORK_NONE; + m_is_server = false; + m_max_players = 4; + m_is_registered = false; + m_server_name = ""; + m_password = ""; + m_server_discovery_port = 2757; + m_server_port = 2758; + m_client_port = 2759; m_my_address.lock(); m_my_address.getData().clear(); m_my_address.unlock(); @@ -56,3 +58,11 @@ void NetworkConfig::setMyAddress(const TransportAddress& addr) m_my_address.unlock(); } // setPublicAddress +// -------------------------------------------------------------------- +/** Sets if this instance is a server or client. It also assigns the + * private port depending if this is a server or client. + */ +void NetworkConfig::setIsServer(bool b) +{ + m_is_server = b; +} // setIsServer diff --git a/src/network/network_config.hpp b/src/network/network_config.hpp index f09b93d5e..d57903c3e 100644 --- a/src/network/network_config.hpp +++ b/src/network/network_config.hpp @@ -50,10 +50,14 @@ private: * be updated from a separate thread. */ Synchronised m_my_address; - /** Even if this is a WAN server, we also store the private (LAN) - * port number, to allow direct connection to clients on the same - * LAN. */ - uint16_t m_private_port; + /** The port number to which the server listens to detect LAN requests. */ + uint16_t m_server_discovery_port; + + /** The port on which the server listens for connection requests from LAN. */ + uint16_t m_server_port; + + /** The LAN port on which a client is waiting for a server connection. */ + uint16_t m_client_port; /** Maximum number of players on the server. */ int m_max_players; @@ -85,7 +89,28 @@ public: // ------------------------------------------------------------------------ void setMyAddress(const TransportAddress& addr); - + void setIsServer(bool b); + // ------------------------------------------------------------------------ + /** Sets the port for server discovery. */ + void setServerDiscoveryPort(uint16_t port) + { + m_server_discovery_port = port; + } // setServerDiscoveryPort + // ------------------------------------------------------------------------ + /** Sets the port on which this server listens. */ + void setServerPort(uint16_t port) { m_server_port = port; } + // ------------------------------------------------------------------------ + /** Sets the port on which a client listens for server connection. */ + void setClientPort(uint16_t port) { m_client_port = port; } + // ------------------------------------------------------------------------ + /** Returns the port on which this server listenes. */ + uint16_t getServerPort() const { return m_server_port; } + // ------------------------------------------------------------------------ + /** Returns the port for LAN server discovery. */ + uint16_t getServerDiscoveryPort() const { return m_server_discovery_port; } + // ------------------------------------------------------------------------ + /** Returns the port on which a client listens for server connections. */ + uint16_t getClientPort() const { return m_client_port; } // ------------------------------------------------------------------------ /** Sets the password for a server. */ void setPassword(const std::string &password) { m_password = password; } @@ -118,9 +143,6 @@ public: /** Returns the maximum number of players for this server. */ int getMaxPlayers() const { return m_max_players; } // -------------------------------------------------------------------- - /** Sets if this instance is a server or client. */ - void setIsServer(bool b) { m_is_server = b; } - // -------------------------------------------------------------------- /** Returns if this instance is a server. */ bool isServer() const { return m_is_server; } // -------------------------------------------------------------------- @@ -158,6 +180,7 @@ public: /** Returns the IP address of this host. We need to return a copy * to make sure the address is thread safe (otherwise it could happen * that e.g. data is taken when the IP address was written, but not + return a; * yet the port). */ const TransportAddress getMyAddress() const { @@ -167,12 +190,6 @@ public: m_my_address.unlock(); return a; } // getMyAddress - // ------------------------------------------------------------------------ - /** Sets the private (LAN) port for this instance. */ - void setPrivatePort(uint16_t port) { m_private_port = port; } - // ------------------------------------------------------------------------ - /** Returns the private (LAN) port. */ - uint16_t getPrivatePort() const { return m_private_port; } }; // class NetworkConfig diff --git a/src/network/protocols/connect_to_peer.cpp b/src/network/protocols/connect_to_peer.cpp index 2d40c14d5..45a38bc55 100644 --- a/src/network/protocols/connect_to_peer.cpp +++ b/src/network/protocols/connect_to_peer.cpp @@ -71,6 +71,8 @@ ConnectToPeer::~ConnectToPeer() void ConnectToPeer::setup() { + m_broadcast_count = 0; + m_time_last_broadcast = 0; } // setup // ---------------------------------------------------------------------------- @@ -125,14 +127,40 @@ void ConnectToPeer::asynchronousUpdate() // the server itself accepts connections from anywhere. if (!m_is_lan && m_peer_address.getIP() != NetworkConfig::get() - ->getMyAddress().getIP()) + ->getMyAddress().getIP()) { m_current_protocol = new PingProtocol(m_peer_address, /*time-between-ping*/2.0); ProtocolManager::getInstance()->requestStart(m_current_protocol); m_state = CONNECTING; + } + else + { + m_broadcast_count = 0; + // Make sure we trigger the broadcast operation next + m_time_last_broadcast = float(StkTime::getRealTime()-100.0f); + m_state = WAIT_FOR_LAN; + } + break; + } + case WAIT_FOR_LAN: + { + // Broadcast once per second + if (StkTime::getRealTime() < m_time_last_broadcast + 1.0f) + { break; } + m_time_last_broadcast = float(StkTime::getRealTime()); + m_broadcast_count++; + if (m_broadcast_count > 100) + { + // Not much we can do about if we don't receive the client + // connection - it could have stopped, lost network, ... + // Terminate this protocol. + Log::error("ConnectToPeer", "Time out trying to connect to %s", + m_peer_address.toString().c_str()); + requestTerminate(); + } // Otherwise we are in the same LAN (same public ip address). // Just send a broadcast packet with the string aloha_stk inside, @@ -146,6 +174,9 @@ void ConnectToPeer::asynchronousUpdate() else broadcast_address.copy(m_peer_address); + + broadcast_address.copy(m_peer_address); + BareNetworkString aloha(std::string("aloha_stk")); STKHost::get()->sendRawPacket(aloha, broadcast_address); Log::info("ConnectToPeer", "Broadcast aloha sent."); @@ -155,10 +186,12 @@ void ConnectToPeer::asynchronousUpdate() broadcast_address.setPort(m_peer_address.getPort()); STKHost::get()->sendRawPacket(aloha, broadcast_address); Log::info("ConnectToPeer", "Broadcast aloha to self."); - m_state = CONNECTING; break; } case CONNECTING: // waiting for the peer to connect + // If we receive a 'connected' event from enet, our + // notifyEventAsynchronous is called, which will move + // the FSM to the next state CONNECTED break; case CONNECTED: { diff --git a/src/network/protocols/connect_to_peer.hpp b/src/network/protocols/connect_to_peer.hpp index b83080b48..6f5532fcf 100644 --- a/src/network/protocols/connect_to_peer.hpp +++ b/src/network/protocols/connect_to_peer.hpp @@ -39,10 +39,17 @@ protected: /** True if this is a LAN connection. */ bool m_is_lan; + /** We might need to broadcast several times (in case the client is not + * ready in time). This keep track of broadcastst. */ + float m_time_last_broadcast; + + int m_broadcast_count; + enum STATE { NONE, RECEIVED_PEER_ADDRESS, + WAIT_FOR_LAN, CONNECTING, CONNECTED, DONE, diff --git a/src/network/protocols/connect_to_server.cpp b/src/network/protocols/connect_to_server.cpp index 9ef2acb0d..28d94812d 100644 --- a/src/network/protocols/connect_to_server.cpp +++ b/src/network/protocols/connect_to_server.cpp @@ -284,7 +284,7 @@ void ConnectToServer::registerWithSTKServer() request->addParameter("address", addr.getIP()); request->addParameter("port", addr.getPort()); request->addParameter("private_port", - NetworkConfig::get()->getPrivatePort()); + NetworkConfig::get()->getClientPort()); Log::info("ConnectToServer", "Registering addr %s", addr.toString().c_str()); diff --git a/src/network/protocols/get_public_address.cpp b/src/network/protocols/get_public_address.cpp index 955c16589..1a82c69cd 100644 --- a/src/network/protocols/get_public_address.cpp +++ b/src/network/protocols/get_public_address.cpp @@ -24,6 +24,7 @@ #include "network/network_string.hpp" #include "network/protocols/connect_to_server.hpp" #include "utils/log.hpp" +#include "utils/string_utils.hpp" #include #include @@ -43,8 +44,35 @@ #include // make the linker happy -const uint32_t GetPublicAddress::m_stun_magic_cookie = 0x2112A442; +const uint32_t GetPublicAddress::m_stun_magic_cookie = 0x2112A442; +TransportAddress GetPublicAddress::m_my_address(0, 0); +void GetPublicAddress::setMyIPAddress(const std::string &s) +{ + std::vector l = StringUtils::split(s, ':'); + if (l.size() != 2) + { + Log::fatal("Invalid IP address '%s'.", s.c_str()); + } + std::vector ip = StringUtils::split(l[0], '.'); + if (ip.size() != 4) + { + Log::fatal("Invalid IP address '%s'.", s.c_str()); + } + uint32_t u = 0; + for (unsigned int i = 0; i < 4; i++) + { + int k; + StringUtils::fromString(ip[i], k); + u = (u << 8) + k; + } + m_my_address.setIP(u); + int p; + StringUtils::fromString(l[1], p); + m_my_address.setPort(p); +} // setMyIPAddress + +// ============================================================================ GetPublicAddress::GetPublicAddress(CallbackObject *callback) : Protocol(PROTOCOL_SILENT, callback) { @@ -203,6 +231,22 @@ std::string GetPublicAddress::parseStunResponse() * selected STUN server and then parsing and validating the response */ void GetPublicAddress::asynchronousUpdate() { + // If the user has specified an address, use it instead of the stun protocol. + if (m_my_address.getIP() != 0 && m_my_address.getPort() != 0) + { + NetworkConfig::get()->setMyAddress(m_my_address); + m_state = EXITING; + requestTerminate(); + } +//#define LAN_TEST +#ifdef LAN_TEST + TransportAddress address(0x7f000001, 4); + NetworkConfig::get()->setMyAddress(address); + m_state = EXITING; + requestTerminate(); + return; +#endif + if (m_state == NOTHING_DONE) { createStunRequest(); diff --git a/src/network/protocols/get_public_address.hpp b/src/network/protocols/get_public_address.hpp index 783597d04..d759f29a5 100644 --- a/src/network/protocols/get_public_address.hpp +++ b/src/network/protocols/get_public_address.hpp @@ -20,6 +20,7 @@ #define GET_PUBLIC_ADDRESS_HPP #include "network/protocol.hpp" +#include "network/transport_address.hpp" #include "utils/cpp2011.hpp" #include @@ -36,6 +37,10 @@ private: static const uint32_t m_stun_magic_cookie; static const int m_stun_server_port = 3478; + /** The user can specify its own IP address to make the use of stun + * unnecessary (though that means that the user has to take care of + * opening the firewall). */ + static TransportAddress m_my_address; enum State { NOTHING_DONE, @@ -48,8 +53,9 @@ private: Network* m_transaction_host; public: - GetPublicAddress(CallbackObject *callback = NULL); - virtual ~GetPublicAddress() {} + static void setMyIPAddress(const std::string &s); + GetPublicAddress(CallbackObject *callback = NULL); + virtual ~GetPublicAddress() {} virtual void asynchronousUpdate() OVERRIDE; // ------------------------------------------------------------------------ @@ -60,6 +66,7 @@ public: virtual bool notifyEventAsynchronous(Event* event) OVERRIDE { return true; } // ------------------------------------------------------------------------ virtual void setup() { m_state = NOTHING_DONE; } + // ------------------------------------------------------------------------ }; // class GetPublicAddress diff --git a/src/network/protocols/server_lobby_room_protocol.cpp b/src/network/protocols/server_lobby_room_protocol.cpp index cd8462041..f17279476 100644 --- a/src/network/protocols/server_lobby_room_protocol.cpp +++ b/src/network/protocols/server_lobby_room_protocol.cpp @@ -210,7 +210,7 @@ void ServerLobbyRoomProtocol::registerServer() request->addParameter("address", addr.getIP() ); request->addParameter("port", addr.getPort() ); request->addParameter("private_port", - NetworkConfig::get()->getPrivatePort()); + NetworkConfig::get()->getServerPort() ); request->addParameter("name", NetworkConfig::get()->getServerName() ); request->addParameter("max_players", UserConfigParams::m_server_max_players ); diff --git a/src/network/servers_manager.cpp b/src/network/servers_manager.cpp index 3fa08d4fb..4dcfe0c27 100644 --- a/src/network/servers_manager.cpp +++ b/src/network/servers_manager.cpp @@ -153,7 +153,8 @@ Online::XMLRequest* ServersManager::getLANRefreshRequest() const Network *broadcast = new Network(1, 1, 0, 0); BareNetworkString s(std::string("stk-server")); - TransportAddress broadcast_address(-1, 2757); + TransportAddress broadcast_address(-1, + NetworkConfig::get()->getServerDiscoveryPort()); broadcast->sendRawPacket(s, broadcast_address); Log::info("ServersManager", "Sent broadcast message."); diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index 5dbe7a7ea..9f4ec911e 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -141,7 +141,7 @@ void STKHost::create() * * Server: * - * The ServerLobbyRoomProtocol (SLR) will the detect the above client + * The ServerLobbyRoomProtocol (SLR) will then detect the above client * requests, and start a ConnectToPeer protocol for each incoming client. * The ConnectToPeer protocol uses: * 1. GetPeerAddress to get the ip address and port of the client. @@ -231,9 +231,13 @@ STKHost::STKHost(uint32_t server_id, uint32_t host_id) // server is made. m_host_id = 0; init(); + TransportAddress a; + a.setIP(0); + a.setPort(NetworkConfig::get()->getClientPort()); + ENetAddress ea = a.toEnetAddress(); m_network = new Network(/*peer_count*/1, /*channel_limit*/2, - /*max_in_bandwidth*/0, /*max_out_bandwidth*/0); + /*max_in_bandwidth*/0, /*max_out_bandwidth*/0, &ea); if (!m_network) { Log::fatal ("STKHost", "An error occurred while trying to create " @@ -258,7 +262,7 @@ STKHost::STKHost(const irr::core::stringw &server_name) ENetAddress addr; addr.host = STKHost::HOST_ANY; - addr.port = 2758; + addr.port = NetworkConfig::get()->getServerPort(); m_network= new Network(NetworkConfig::get()->getMaxPlayers(), /*channel_limit*/2, @@ -511,7 +515,7 @@ void* STKHost::mainLoop(void* self) if(NetworkConfig::get()->isServer() && NetworkConfig::get()->isLAN() ) { - TransportAddress address(0, 2757); + TransportAddress address(0, NetworkConfig::get()->getServerDiscoveryPort()); ENetAddress eaddr = address.toEnetAddress(); myself->m_lan_network = new Network(1, 1, 0, 0, &eaddr); } @@ -565,6 +569,10 @@ void* STKHost::mainLoop(void* self) } // mainLoop // ---------------------------------------------------------------------------- +/** Handles LAN related messages. It checks for any LAN broadcast messages, + * and if a valid LAN server-request message is received, will answer + * with a message containing server details (and sender IP address and port). + */ void STKHost::handleLANRequests() { const int LEN=2048; From a38c8f0c195ddec6cd9eaebd66d206ce8a1dea36 Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 26 Oct 2016 00:34:13 +0800 Subject: [PATCH 340/350] Make billboard text display properly when viewing in any direction --- src/graphics/stk_text_billboard.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/graphics/stk_text_billboard.cpp b/src/graphics/stk_text_billboard.cpp index b3d82540d..9884272f0 100644 --- a/src/graphics/stk_text_billboard.cpp +++ b/src/graphics/stk_text_billboard.cpp @@ -54,7 +54,9 @@ void STKTextBillboard::updateAbsolutePosition() { // Override to not use the parent's rotation AbsoluteTransformation = getRelativeTransformation(); - AbsoluteTransformation.setTranslation(AbsoluteTransformation.getTranslation() + Parent->getAbsolutePosition()); + core::vector3df wc = RelativeTranslation; + Parent->getAbsoluteTransformation().transformVect(wc); + AbsoluteTransformation.setTranslation(wc); } else AbsoluteTransformation = getRelativeTransformation(); @@ -173,11 +175,15 @@ scene::IMesh* STKTextBillboard::getTextMesh(core::stringw text, FontWithFace* fo void STKTextBillboard::updateNoGL() { - scene::ICameraSceneNode* curr_cam = irr_driver->getSceneManager()->getActiveCamera(); - core::vector3df cam_pos = curr_cam->getPosition(); - core::vector3df text_pos = this->getAbsolutePosition(); - float angle = atan2(text_pos.X - cam_pos.X, text_pos.Z - cam_pos.Z); - this->setRotation(core::vector3df(0.0f, angle * 180.0f / M_PI, 0.0f)); + scene::ICameraSceneNode* curr_cam =irr_driver->getSceneManager()->getActiveCamera(); + btMatrix3x3 m; + m.setFromOpenGLSubMatrix(curr_cam->getViewMatrix().pointer()); + btQuaternion q; + m.getRotation(q); + q.setW(-q.getW()); + Vec3 hpr; + hpr.setHPR(q); + this->setRotation(hpr.toIrrHPR()); updateAbsolutePosition(); STKMeshSceneNode::updateNoGL(); From cc3ff7cf9f23154f435210b45a0823e579dc9b94 Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 26 Oct 2016 09:08:10 +0800 Subject: [PATCH 341/350] Simplify calculation of transform --- src/graphics/stk_text_billboard.cpp | 32 +++++++++++++---------------- src/graphics/stk_text_billboard.hpp | 2 -- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/graphics/stk_text_billboard.cpp b/src/graphics/stk_text_billboard.cpp index 9884272f0..fcd52497b 100644 --- a/src/graphics/stk_text_billboard.cpp +++ b/src/graphics/stk_text_billboard.cpp @@ -50,16 +50,28 @@ STKTextBillboard::STKTextBillboard(core::stringw text, FontWithFace* font, void STKTextBillboard::updateAbsolutePosition() { + // Make billboard always face the camera + scene::ICameraSceneNode* curr_cam = + irr_driver->getSceneManager()->getActiveCamera(); + if (!curr_cam) return; + core::quaternion q(curr_cam->getViewMatrix()); + q.W = -q.W; + if (Parent) { // Override to not use the parent's rotation - AbsoluteTransformation = getRelativeTransformation(); core::vector3df wc = RelativeTranslation; Parent->getAbsoluteTransformation().transformVect(wc); AbsoluteTransformation.setTranslation(wc); + q.getMatrix(AbsoluteTransformation, wc); } else - AbsoluteTransformation = getRelativeTransformation(); + { + q.getMatrix(AbsoluteTransformation, RelativeTranslation); + } + core::matrix4 m; + m.setScale(RelativeScale); + AbsoluteTransformation *= m; } scene::IMesh* STKTextBillboard::getTextMesh(core::stringw text, FontWithFace* font) @@ -173,22 +185,6 @@ scene::IMesh* STKTextBillboard::getTextMesh(core::stringw text, FontWithFace* fo return Mesh; } -void STKTextBillboard::updateNoGL() -{ - scene::ICameraSceneNode* curr_cam =irr_driver->getSceneManager()->getActiveCamera(); - btMatrix3x3 m; - m.setFromOpenGLSubMatrix(curr_cam->getViewMatrix().pointer()); - btQuaternion q; - m.getRotation(q); - q.setW(-q.getW()); - Vec3 hpr; - hpr.setHPR(q); - this->setRotation(hpr.toIrrHPR()); - updateAbsolutePosition(); - - STKMeshSceneNode::updateNoGL(); -} - void STKTextBillboard::collectChar(video::ITexture* texture, const core::rect& destRect, const core::rect& sourceRect, diff --git a/src/graphics/stk_text_billboard.hpp b/src/graphics/stk_text_billboard.hpp index 89e41f6ae..90cfddbb4 100644 --- a/src/graphics/stk_text_billboard.hpp +++ b/src/graphics/stk_text_billboard.hpp @@ -74,8 +74,6 @@ public: const irr::core::vector3df& position, const irr::core::vector3df& size); - virtual void updateNoGL() OVERRIDE; - virtual scene::ESCENE_NODE_TYPE getType() const OVERRIDE { return scene::ESNT_TEXT; From 9bd4c87b9ccc45e3428857053670bbd0615e9f75 Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 26 Oct 2016 09:21:17 +0800 Subject: [PATCH 342/350] Fix leaking createTextBillboard --- src/scriptengine/script_track.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/scriptengine/script_track.cpp b/src/scriptengine/script_track.cpp index 16f00e914..7b3b4fb68 100644 --- a/src/scriptengine/script_track.cpp +++ b/src/scriptengine/script_track.cpp @@ -105,6 +105,7 @@ namespace Scripting core::vector3df(1.5f, 1.5f, 1.5f)); World::getWorld()->getTrack()->addNode(tb); + tb->drop(); } else { From e93e8b7708f010db41a6fb59e88f81e3f566aa27 Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 26 Oct 2016 14:25:48 +0800 Subject: [PATCH 343/350] Make animated mesh render separately in instanced rendering Try to introduce mesh + render info hash in vao manager, without copying the mesh --- src/graphics/stk_animated_mesh.cpp | 16 ++++++++++++--- src/graphics/stk_scene_manager.cpp | 23 --------------------- src/graphics/vao_manager.cpp | 22 ++++++++++---------- src/graphics/vao_manager.hpp | 32 +++++++++++++++++++++++++++--- src/karts/kart_model.cpp | 31 +++++++---------------------- 5 files changed, 61 insertions(+), 63 deletions(-) diff --git a/src/graphics/stk_animated_mesh.cpp b/src/graphics/stk_animated_mesh.cpp index 918b19c0d..f617a1665 100644 --- a/src/graphics/stk_animated_mesh.cpp +++ b/src/graphics/stk_animated_mesh.cpp @@ -99,6 +99,16 @@ void STKAnimatedMesh::updateNoGL() if (!isMaterialInitialized) { + // Use a default render info to distinguish same mesh buffer created by + // different animated mesh node in vao manager when using instanced + // rendering + RenderInfo* default_ri = NULL; + if (CVS->isARBBaseInstanceUsable()) + { + default_ri = new RenderInfo(); + m_static_render_info.push_back(default_ri); + } + video::IVideoDriver* driver = SceneManager->getVideoDriver(); const u32 mb_count = m->getMeshBufferCount(); for (u32 i = 0; i < mb_count; ++i) @@ -121,7 +131,7 @@ void STKAnimatedMesh::updateNoGL() } else { - cur_ri = NULL; + cur_ri = default_ri; } } else @@ -137,7 +147,7 @@ void STKAnimatedMesh::updateNoGL() assert(cur_ri ? cur_ri->isStatic() : true); GLmeshes.push_back(allocateMeshBuffer(mb, m_debug_name, affected || m_all_parts_colorized || (cur_ri - && cur_ri->isTransparent()) ? cur_ri : NULL)); + && cur_ri->isTransparent()) ? cur_ri : default_ri)); } for (u32 i = 0; i < m->getMeshBufferCount(); ++i) @@ -221,7 +231,7 @@ void STKAnimatedMesh::updateGL() if (CVS->isARBBaseInstanceUsable()) { - std::pair p = VAOManager::getInstance()->getBase(mb); + std::pair p = VAOManager::getInstance()->getBase(mb, GLmeshes[i].m_render_info); mesh.vaoBaseVertex = p.first; mesh.vaoOffset = p.second; } diff --git a/src/graphics/stk_scene_manager.cpp b/src/graphics/stk_scene_manager.cpp index 5ddfa53f6..d75f33187 100644 --- a/src/graphics/stk_scene_manager.cpp +++ b/src/graphics/stk_scene_manager.cpp @@ -41,7 +41,6 @@ #include #include -#include template struct InstanceFiller @@ -156,28 +155,6 @@ FillInstances_impl(std::vector > Instan PolyCount += (InstanceBufferOffset - InitialOffset) * mesh->IndexCount / 3; } -class MeshRenderInfoHash -{ -public: - size_t operator() (const std::pair &p) const - { - return (std::hash()(p.first) ^ - (std::hash()(p.second) << 1)); - } -}; - -struct MeshRenderInfoEquals : std::binary_function - &, - const std::pair&, bool> -{ - result_type operator() (first_argument_type lhs, - second_argument_type rhs) const - { - return (lhs.first == rhs.first) && - (lhs.second == rhs.second); - } -}; - template static void FillInstances(const std::unordered_map, std::vector >, MeshRenderInfoHash, MeshRenderInfoEquals> &GatheredGLMesh, std::vector &InstancedList, diff --git a/src/graphics/vao_manager.cpp b/src/graphics/vao_manager.cpp index 7d69b3238..6448488be 100644 --- a/src/graphics/vao_manager.cpp +++ b/src/graphics/vao_manager.cpp @@ -287,7 +287,7 @@ irr::video::E_VERTEX_TYPE VAOManager::getVertexType(enum VTXTYPE tp) } } -void VAOManager::append(scene::IMeshBuffer *mb, VTXTYPE tp) +void VAOManager::append(scene::IMeshBuffer *mb, VTXTYPE tp, RenderInfo* ri) { size_t old_vtx_cnt = last_vertex[tp]; size_t old_idx_cnt = last_index[tp]; @@ -318,26 +318,28 @@ void VAOManager::append(scene::IMeshBuffer *mb, VTXTYPE tp) glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, old_idx_cnt * sizeof(u16), mb->getIndexCount() * sizeof(u16), mb->getIndices()); } - mappedBaseVertex[tp][mb] = old_vtx_cnt; - mappedBaseIndex[tp][mb] = old_idx_cnt * sizeof(u16); + std::pair key(mb, ri); + mappedBaseVertex[tp][key] = old_vtx_cnt; + mappedBaseIndex[tp][key] = old_idx_cnt * sizeof(u16); } -std::pair VAOManager::getBase(scene::IMeshBuffer *mb) +std::pair VAOManager::getBase(scene::IMeshBuffer *mb, RenderInfo* ri) { VTXTYPE tp = getVTXTYPE(mb->getVertexType()); - if (mappedBaseVertex[tp].find(mb) == mappedBaseVertex[tp].end()) + std::pair key(mb, ri); + if (mappedBaseVertex[tp].find(key) == mappedBaseVertex[tp].end()) { - assert(mappedBaseIndex[tp].find(mb) == mappedBaseIndex[tp].end()); - append(mb, tp); + assert(mappedBaseIndex[tp].find(key) == mappedBaseIndex[tp].end()); + append(mb, tp, ri); regenerateVAO(tp); regenerateInstancedVAO(); } - std::unordered_map::iterator It; - It = mappedBaseVertex[tp].find(mb); + std::unordered_map, unsigned, MeshRenderInfoHash, MeshRenderInfoEquals>::iterator It; + It = mappedBaseVertex[tp].find(key); assert(It != mappedBaseVertex[tp].end()); unsigned vtx = It->second; - It = mappedBaseIndex[tp].find(mb); + It = mappedBaseIndex[tp].find(key); assert(It != mappedBaseIndex[tp].end()); return std::pair(vtx, It->second); } diff --git a/src/graphics/vao_manager.hpp b/src/graphics/vao_manager.hpp index e82235841..e28491905 100644 --- a/src/graphics/vao_manager.hpp +++ b/src/graphics/vao_manager.hpp @@ -26,6 +26,8 @@ #include #include +class RenderInfo; + enum InstanceType { InstanceTypeDualTex, @@ -153,6 +155,30 @@ struct GlowInstanceData #pragma pack(pop) #endif +#include + +class MeshRenderInfoHash +{ +public: + size_t operator() (const std::pair &p) const + { + return (std::hash()(p.first) ^ + (std::hash()(p.second) << 1)); + } +}; + +struct MeshRenderInfoEquals : std::binary_function + &, + const std::pair&, bool> +{ + result_type operator() (first_argument_type lhs, + second_argument_type rhs) const + { + return (lhs.first == rhs.first) && + (lhs.second == rhs.second); + } +}; + class VAOManager : public Singleton { enum VTXTYPE { VTXTYPE_STANDARD, VTXTYPE_TCOORD, VTXTYPE_TANGENT, VTXTYPE_COUNT }; @@ -162,7 +188,7 @@ class VAOManager : public Singleton void *VBOPtr[VTXTYPE_COUNT], *IBOPtr[VTXTYPE_COUNT]; size_t RealVBOSize[VTXTYPE_COUNT], RealIBOSize[VTXTYPE_COUNT]; size_t last_vertex[VTXTYPE_COUNT], last_index[VTXTYPE_COUNT]; - std::unordered_map mappedBaseVertex[VTXTYPE_COUNT], mappedBaseIndex[VTXTYPE_COUNT]; + std::unordered_map , unsigned, MeshRenderInfoHash, MeshRenderInfoEquals> mappedBaseVertex[VTXTYPE_COUNT], mappedBaseIndex[VTXTYPE_COUNT]; std::map, GLuint> InstanceVAO; void cleanInstanceVAOs(); @@ -172,10 +198,10 @@ class VAOManager : public Singleton size_t getVertexPitch(enum VTXTYPE) const; VTXTYPE getVTXTYPE(irr::video::E_VERTEX_TYPE type); irr::video::E_VERTEX_TYPE getVertexType(enum VTXTYPE tp); - void append(irr::scene::IMeshBuffer *, VTXTYPE tp); + void append(irr::scene::IMeshBuffer *, VTXTYPE tp, RenderInfo* ri = NULL); public: VAOManager(); - std::pair getBase(irr::scene::IMeshBuffer *); + std::pair getBase(irr::scene::IMeshBuffer *, RenderInfo* ri = NULL); GLuint getInstanceBuffer(InstanceType it) { return instance_vbo[it]; } void *getInstanceBufferPtr(InstanceType it) { return Ptr[it]; } unsigned getVBO(irr::video::E_VERTEX_TYPE type) { return vbo[getVTXTYPE(type)]; } diff --git a/src/karts/kart_model.cpp b/src/karts/kart_model.cpp index 9517d344d..642a533fe 100644 --- a/src/karts/kart_model.cpp +++ b/src/karts/kart_model.cpp @@ -247,12 +247,6 @@ KartModel::~KartModel() { // Master KartModels should never have a speed weighted object attached. assert(!m_is_master); - - // Drop the cloned transparent model if created - if (m_krt == KRT_TRANSPARENT) - { - m_speed_weighted_objects[i].m_model->drop(); - } m_speed_weighted_objects[i].m_node->drop(); } if(m_is_master && m_speed_weighted_objects[i].m_model) @@ -262,20 +256,15 @@ KartModel::~KartModel() } } - // In case of the master, the mesh must be dropped. A non-master KartModel - // has a copy of the master's mesh, so it needs to be dropped, too. - if (m_mesh) + if (m_is_master && m_mesh) { m_mesh->drop(); - if (m_is_master) + // If there is only one copy left, it's the copy in irrlicht's + // mesh cache, so it can be removed. + if (m_mesh && m_mesh->getReferenceCount() == 1) { - // If there is only one copy left, it's the copy in irrlicht's - // mesh cache, so it can be removed. - if (m_mesh && m_mesh->getReferenceCount() == 1) - { - irr_driver->dropAllTextures(m_mesh); - irr_driver->removeMeshFromCache(m_mesh); - } + irr_driver->dropAllTextures(m_mesh); + irr_driver->removeMeshFromCache(m_mesh); } } @@ -308,7 +297,7 @@ KartModel* KartModel::makeCopy(KartRenderType krt) km->m_kart_height = m_kart_height; km->m_kart_highest_point = m_kart_highest_point; km->m_kart_lowest_point = m_kart_lowest_point; - km->m_mesh = irr_driver->copyAnimatedMesh(m_mesh); + km->m_mesh = m_mesh; km->m_model_filename = m_model_filename; km->m_animation_speed = m_animation_speed; km->m_current_animation = AF_DEFAULT; @@ -343,12 +332,6 @@ KartModel* KartModel::makeCopy(KartRenderType krt) // Master should not have any speed weighted nodes. assert(!m_speed_weighted_objects[i].m_node); km->m_speed_weighted_objects[i] = m_speed_weighted_objects[i]; - if (krt == KRT_TRANSPARENT) - { - // Only clone the mesh if transparent type is used, see #2445 - km->m_speed_weighted_objects[i].m_model = irr_driver - ->copyAnimatedMesh(m_speed_weighted_objects[i].m_model); - } } for(unsigned int i=AF_BEGIN; i<=AF_END; i++) From 8d270e5ff3d2563942ec5d98d0e4f957de9f5b53 Mon Sep 17 00:00:00 2001 From: deve Date: Wed, 26 Oct 2016 13:51:17 +0200 Subject: [PATCH 344/350] Fixed nitro bar for non-HD textures --- src/states_screens/race_gui.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/states_screens/race_gui.cpp b/src/states_screens/race_gui.cpp index 8012947ef..5b5b43c1e 100644 --- a/src/states_screens/race_gui.cpp +++ b/src/states_screens/race_gui.cpp @@ -182,7 +182,7 @@ void RaceGUI::renderGlobal(float dt) //stop displaying timer as soon as race is over if (world->getPhase()getPhase() == WorldStatus::GO_PHASE || world->getPhase() == WorldStatus::MUSIC_PHASE) { @@ -210,7 +210,7 @@ void RaceGUI::renderPlayerView(const Camera *camera, float dt) core::vector2df scaling = camera->getScaling(); const AbstractKart *kart = camera->getKart(); if(!kart) return; - + drawPlungerInFace(camera, dt); scaling *= viewport.getWidth()/800.0f; // scale race GUI along screen size @@ -453,7 +453,8 @@ void RaceGUI::drawEnergyMeter(int x, int y, const AbstractKart *kart, (int)offset.Y-gauge_height, (int)offset.X + gauge_width, (int)offset.Y) /* dest rect */, - core::rect(0, 0, 256, 256) /* source rect */, + core::rect(core::position2d(0,0), + m_gauge_empty->getSize()) /* source rect */, NULL /* clip rect */, NULL /* colors */, true /* alpha */); @@ -845,7 +846,7 @@ void RaceGUI::drawLap(const AbstractKart* kart, { // Don't display laps or ranks if the kart has already finished the race. if (kart->hasFinishedRace()) return; - + World *world = World::getWorld(); if (!world->raceHasLaps()) return; const int lap = world->getKartLaps(kart->getWorldKartId()); From b1f6632e5b23a5f20580d564b3e0712e2e354d9c Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 27 Oct 2016 08:40:45 +1100 Subject: [PATCH 345/350] Changed countdown from a mixture of ms and seconds to seconds only; added documentation. --- src/main_loop.cpp | 16 +++++++-- src/network/protocols/start_game_protocol.cpp | 9 +++-- .../protocols/synchronization_protocol.cpp | 36 +++++++++++++------ .../protocols/synchronization_protocol.hpp | 14 +++++--- src/network/stk_host.cpp | 17 ++++++++- 5 files changed, 71 insertions(+), 21 deletions(-) diff --git a/src/main_loop.cpp b/src/main_loop.cpp index 29d9a1fad..2db85e3e5 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -34,6 +34,7 @@ #include "network/protocol_manager.hpp" #include "network/race_event_manager.hpp" #include "network/stk_host.hpp" +#include "network/protocols/synchronization_protocol.hpp" #include "online/request_manager.hpp" #include "race/history.hpp" #include "race/race_manager.hpp" @@ -288,9 +289,18 @@ void MainLoop::run() PROFILER_POP_CPU_MARKER(); } - // Update world time if world exists - if (World::getWorld()) - World::getWorld()->updateTime(dt); + if (World::getWorld() ) + { + // In case of networking world we can only start the timing once the + // SynchronizationProtocol has disappeared (which indicates that all + // other protocols necessary for running a game are running). + SynchronizationProtocol* protocol = static_cast( + ProtocolManager::getInstance()->getProtocol(PROTOCOL_SYNCHRONIZATION)); + if (!protocol) + { + World::getWorld()->updateTime(dt); + } + } PROFILER_POP_CPU_MARKER(); PROFILER_SYNC_FRAME(); diff --git a/src/network/protocols/start_game_protocol.cpp b/src/network/protocols/start_game_protocol.cpp index e913b1d10..b5aea6a29 100644 --- a/src/network/protocols/start_game_protocol.cpp +++ b/src/network/protocols/start_game_protocol.cpp @@ -59,7 +59,7 @@ void StartGameProtocol::setup() // This creates the network world. RaceEventManager::getInstance()->start(); - // The number of karts includes the AI karts, which are not supported atn + // The number of karts includes the AI karts, which are not supported atm race_manager->setNumKarts(m_game_setup->getPlayerCount()); // Set number of global and local players. @@ -121,6 +121,11 @@ void StartGameProtocol::setup() } // setup // ---------------------------------------------------------------------------- +/** Handles incoming messages on the server. Should never be called on a + * client. It counts how many clients are ready, and once all clients + * are ready, will call startRace(). + * \param event Details about the received mnessage. + */ bool StartGameProtocol::notifyEventAsynchronous(Event* event) { if(!checkDataSize(event, 1)) return true; @@ -157,7 +162,7 @@ void StartGameProtocol::startRace() static_cast(p); if (protocol) { - protocol->startCountdown(5000); // 5 seconds countdown + protocol->startCountdown(5.0f); // 5 seconds countdown Log::info("StartGameProtocol", "All players ready, starting countdown."); m_ready = true; diff --git a/src/network/protocols/synchronization_protocol.cpp b/src/network/protocols/synchronization_protocol.cpp index c553b79ad..c34334239 100644 --- a/src/network/protocols/synchronization_protocol.cpp +++ b/src/network/protocols/synchronization_protocol.cpp @@ -37,7 +37,12 @@ void SynchronizationProtocol::setup() m_has_quit = false; } // setup //----------------------------------------------------------------------------- - +/** Called when receiving a message. On the client side the message is a ping + * from the server, which is answered back. The client will also check if the + * server has started the countdown (which is indicated in the ping message). + * On the server the received message is a reply to a previous ping request. + * The server will keep track of average latency. + */ bool SynchronizationProtocol::notifyEventAsynchronous(Event* event) { if (event->getType() != EVENT_TYPE_MESSAGE) @@ -79,15 +84,15 @@ bool SynchronizationProtocol::notifyEventAsynchronous(Event* event) // countdown time in the message if (data.size() == 4) { - uint32_t time_to_start = data.getUInt32(); + float time_to_start = data.getFloat(); Log::debug("SynchronizationProtocol", - "Request to start game in %d.", time_to_start); + "Request to start game in %f.", time_to_start); if (!m_countdown_activated) startCountdown(time_to_start); else { // Adjust the time based on the value sent from the server. - m_countdown = (double)(time_to_start/1000.0); + m_countdown = time_to_start; } } else @@ -120,10 +125,19 @@ bool SynchronizationProtocol::notifyEventAsynchronous(Event* event) } // notifyEventAsynchronous //----------------------------------------------------------------------------- - +/** Waits for the countdown to be started. On the server the start of the + * countdown is triggered by the StartGameProtocol::startRace(), which is + * called once all clients have confirmed that they are ready to start. + * The server will send a ping request to each client once a second, and + * include the information if the countdown has started (and its current + * value). On the client the countdown is started in notifyEvenAsynchronous() + * when a server ping is received that indicates that the countdown has + * started. The measured times can be used later to estimate the latency + * between server and client. + */ void SynchronizationProtocol::asynchronousUpdate() { - double current_time = StkTime::getRealTime(); + float current_time = float(StkTime::getRealTime()); if (m_countdown_activated) { m_countdown -= (current_time - m_last_countdown_update); @@ -167,7 +181,7 @@ void SynchronizationProtocol::asynchronousUpdate() // message is received), or to update the countdown time. if (m_countdown_activated) { - ping_request->addUInt32((int)(m_countdown*1000.0)); + ping_request->addFloat(m_countdown); Log::debug("SynchronizationProtocol", "CNTActivated: Countdown value : %f", m_countdown); } @@ -191,11 +205,11 @@ void SynchronizationProtocol::asynchronousUpdate() * the countdown has to be started. * \param ms_countdown Countdown to use in ms. */ -void SynchronizationProtocol::startCountdown(int ms_countdown) +void SynchronizationProtocol::startCountdown(float ms_countdown) { - m_countdown_activated = true; - m_countdown = (double)(ms_countdown)/1000.0; - m_last_countdown_update = StkTime::getRealTime(); + m_countdown_activated = true; + m_countdown = ms_countdown; + m_last_countdown_update = float(StkTime::getRealTime()); Log::info("SynchronizationProtocol", "Countdown started with value %f", m_countdown); } // startCountdown diff --git a/src/network/protocols/synchronization_protocol.hpp b/src/network/protocols/synchronization_protocol.hpp index 4190f2b95..ed586fa11 100644 --- a/src/network/protocols/synchronization_protocol.hpp +++ b/src/network/protocols/synchronization_protocol.hpp @@ -17,9 +17,14 @@ private: uint32_t m_pings_count; std::vector m_successed_pings; std::vector m_total_diff; + + /** True if the countdown has started, i.e. all clients have loaded + * the track and karts. */ bool m_countdown_activated; - double m_countdown; - double m_last_countdown_update; + + /** The countdown timer value. */ + float m_countdown; + float m_last_countdown_update; bool m_has_quit; /** Keeps track of last time that an update was sent. */ @@ -33,12 +38,13 @@ public: virtual bool notifyEventAsynchronous(Event* event) OVERRIDE; virtual void setup() OVERRIDE; virtual void asynchronousUpdate() OVERRIDE; - void startCountdown(int ms_countdown); + void startCountdown(float ms_countdown); // ------------------------------------------------------------------------ virtual void update(float dt) OVERRIDE {} // ------------------------------------------------------------------------ - int getCountdown() { return (int)(m_countdown*1000.0); } + /** Returns the current countdown value. */ + float getCountdown() { return m_countdown; } }; // class SynchronizationProtocol diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp index 9f4ec911e..1b0f0842a 100644 --- a/src/network/stk_host.cpp +++ b/src/network/stk_host.cpp @@ -218,7 +218,22 @@ void STKHost::create() * the LocalPlayerController for each kart. Each remote player gets a * NULL ActivePlayer (the ActivePlayer is only used for assigning the input * device to each kart, achievements and highscores, so it's not needed for - * remote players). + * remote players). It will also start the SynchronizationProtocol. + * The StartGameProtocol has a callback ready which is called from world + * when the world is loaded (i.e. track and all karts are ready). When + * this callback is invoked, each client will send a 'ready' message to + * the server's StartGameProtocol. Once the server has received all + * messages in notifyEventAsynchronous(), it will call startCountdown() + * in the SynchronizationProtocol. The SynchronizationProtocol is + * sending regular (once per second) pings to the clients and measure + * the averate latency. Upon starting the countdown this information + * is included in the ping request, so the clients can start the countdown + * at that stage as wellk. + * + * Once the countdown is 0 (or below), the Synchronization Protocol will + * start the protocols: KartUpdateProtocol, ControllerEventsProtocol, + * GameEventsProtocol. Then the SynchronizationProtocol is terminated + * which indicates to the main loop to start the actual game. */ // ============================================================================ From 2c56837ee8c46eb983d00829ca82dd61adea3084 Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 27 Oct 2016 08:41:35 +1100 Subject: [PATCH 346/350] Fixed token handling. --- src/network/protocols/game_events_protocol.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/protocols/game_events_protocol.cpp b/src/network/protocols/game_events_protocol.cpp index 9b0a619ed..0bcf6a8d9 100644 --- a/src/network/protocols/game_events_protocol.cpp +++ b/src/network/protocols/game_events_protocol.cpp @@ -46,7 +46,7 @@ bool GameEventsProtocol::notifyEvent(Event* event) Log::warn("GameEventsProtocol", "Too short message."); return true; } - if ( event->getPeer()->getClientServerToken() != data.getUInt32()) + if ( event->getPeer()->getClientServerToken() != data.getToken()) { Log::warn("GameEventsProtocol", "Bad token."); return true; From b3b9d16ba1b5b600a19106334c6418f562b617e4 Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 27 Oct 2016 08:48:38 +1100 Subject: [PATCH 347/350] Removed unnecessary logging. --- src/network/protocols/synchronization_protocol.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/network/protocols/synchronization_protocol.cpp b/src/network/protocols/synchronization_protocol.cpp index c34334239..a1d202276 100644 --- a/src/network/protocols/synchronization_protocol.cpp +++ b/src/network/protocols/synchronization_protocol.cpp @@ -155,17 +155,6 @@ void SynchronizationProtocol::asynchronousUpdate() requestTerminate(); return; } - static int seconds = -1; - if (seconds == -1) - { - seconds = (int)(ceil(m_countdown)); - } - else if (seconds != (int)(ceil(m_countdown))) - { - seconds = (int)(ceil(m_countdown)); - Log::info("SynchronizationProtocol", "Starting in %d seconds.", - seconds); - } } // if m_countdown_activated if (NetworkConfig::get()->isServer() && current_time > m_last_time+1) From d4e9de1f5373acec89cc373ac02d070244f23c66 Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 27 Oct 2016 09:41:01 +0800 Subject: [PATCH 348/350] Fix non-animated karts --- src/graphics/irr_driver.cpp | 6 ++++-- src/graphics/irr_driver.hpp | 3 ++- src/graphics/stk_mesh_scene_node.cpp | 19 +++++++++++++++---- src/graphics/stk_mesh_scene_node.hpp | 4 +++- src/karts/kart_model.cpp | 10 +++++++--- 5 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index c335a7a03..51cadb2f3 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -1236,7 +1236,8 @@ scene::IMeshSceneNode *IrrDriver::addMesh(scene::IMesh *mesh, const std::string& debug_name, scene::ISceneNode *parent, RenderInfo* render_info, - bool all_parts_colorized) + bool all_parts_colorized, + int frame_for_mesh) { if (!CVS->isGLSL()) return m_scene_manager->addMeshSceneNode(mesh, parent); @@ -1251,7 +1252,8 @@ scene::IMeshSceneNode *IrrDriver::addMesh(scene::IMesh *mesh, core::vector3df(0, 0, 0), core::vector3df(1.0f, 1.0f, 1.0f), true, render_info, - all_parts_colorized); + all_parts_colorized, + frame_for_mesh); node->drop(); return node; diff --git a/src/graphics/irr_driver.hpp b/src/graphics/irr_driver.hpp index 08bac725b..3f78657b5 100644 --- a/src/graphics/irr_driver.hpp +++ b/src/graphics/irr_driver.hpp @@ -393,7 +393,8 @@ public: const std::string& debug_name, scene::ISceneNode *parent = NULL, RenderInfo* render_info = NULL, - bool all_parts_colorized = false); + bool all_parts_colorized = false, + int frame_for_mesh = -1); PerCameraNode *addPerCameraNode(scene::ISceneNode* node, scene::ICameraSceneNode* cam, scene::ISceneNode *parent = NULL); diff --git a/src/graphics/stk_mesh_scene_node.cpp b/src/graphics/stk_mesh_scene_node.cpp index d48c98441..3938d4679 100644 --- a/src/graphics/stk_mesh_scene_node.cpp +++ b/src/graphics/stk_mesh_scene_node.cpp @@ -32,8 +32,8 @@ #include "utils/helpers.hpp" #include "utils/tuple.hpp" -#include #include +#include // ============================================================================ class ColorizeShader : public ShadergetMeshBufferCount(); ++i) + + scene::IAnimatedMesh* am = dynamic_cast(Mesh); + scene::IMesh* m = Mesh; + if (am && m_frame_for_mesh > -1) { - scene::IMeshBuffer* mb = Mesh->getMeshBuffer(i); + // Get the correct frame of animation for animated mesh + m = am->getMesh(m_frame_for_mesh); + } + + for (u32 i = 0; i < m->getMeshBufferCount(); ++i) + { + scene::IMeshBuffer* mb = m->getMeshBuffer(i); if (!mb) continue; GLMesh &mesh = GLmeshes[i]; diff --git a/src/graphics/stk_mesh_scene_node.hpp b/src/graphics/stk_mesh_scene_node.hpp index ae879e7b6..07fc4030f 100644 --- a/src/graphics/stk_mesh_scene_node.hpp +++ b/src/graphics/stk_mesh_scene_node.hpp @@ -29,6 +29,7 @@ class STKMeshSceneNode : public irr::scene::CMeshSceneNode, public STKMeshCommon { protected: PtrVector m_static_render_info; + int m_frame_for_mesh; std::vector GLmeshes; core::matrix4 ModelViewProjectionMatrix; core::vector3df windDir; @@ -58,7 +59,8 @@ public: const irr::core::vector3df& rotation = irr::core::vector3df(0, 0, 0), const irr::core::vector3df& scale = irr::core::vector3df(1.0f, 1.0f, 1.0f), bool createGLMeshes = true, - RenderInfo* render_info = NULL, bool all_parts_colorized = false); + RenderInfo* render_info = NULL, bool all_parts_colorized = false, + int frame_for_mesh = -1); virtual void render(); virtual void setMesh(irr::scene::IMesh* mesh); virtual void OnRegisterSceneNode(); diff --git a/src/karts/kart_model.cpp b/src/karts/kart_model.cpp index 642a533fe..c9e265346 100644 --- a/src/karts/kart_model.cpp +++ b/src/karts/kart_model.cpp @@ -427,8 +427,12 @@ scene::ISceneNode* KartModel::attachModel(bool animated_models, bool always_anim ? m_animation_frame[AF_STRAIGHT] : 0; - scene::IMesh* main_frame = m_mesh->getMesh(straight_frame); - main_frame->setHardwareMappingHint(scene::EHM_STATIC); + scene::IMesh* main_frame = m_mesh; + if (!CVS->isGLSL()) + { + main_frame = m_mesh->getMesh(straight_frame); + main_frame->setHardwareMappingHint(scene::EHM_STATIC); + } std::string debug_name; @@ -437,7 +441,7 @@ scene::ISceneNode* KartModel::attachModel(bool animated_models, bool always_anim #endif node = irr_driver->addMesh(main_frame, debug_name, - NULL /*parent*/, getRenderInfo()); + NULL /*parent*/, getRenderInfo(), false, straight_frame); #ifdef DEBUG node->setName(debug_name.c_str()); From 956aa83a0a34cb30326df04e6ad7ddeafc8e5f3a Mon Sep 17 00:00:00 2001 From: Benau Date: Thu, 27 Oct 2016 10:15:41 +0800 Subject: [PATCH 349/350] Remove unused function --- lib/irrlicht/source/Irrlicht/CSkinnedMesh.cpp | 71 ------------------- lib/irrlicht/source/Irrlicht/CSkinnedMesh.h | 1 - src/graphics/irr_driver.cpp | 22 ------ src/graphics/irr_driver.hpp | 1 - 4 files changed, 95 deletions(-) diff --git a/lib/irrlicht/source/Irrlicht/CSkinnedMesh.cpp b/lib/irrlicht/source/Irrlicht/CSkinnedMesh.cpp index 74e472ca5..92ec49820 100644 --- a/lib/irrlicht/source/Irrlicht/CSkinnedMesh.cpp +++ b/lib/irrlicht/source/Irrlicht/CSkinnedMesh.cpp @@ -1474,77 +1474,6 @@ void CSkinnedMesh::calculateTangents( } } -// ---------------------------------------------------------------------------- -/** Copies a mesh. - */ -CSkinnedMesh *CSkinnedMesh::clone() -{ - - CSkinnedMesh* skinned_mesh = new CSkinnedMesh(); - - for (u32 i = 0; i < getMeshBuffers().size(); i++) - { - SSkinMeshBuffer * buffer = skinned_mesh->addMeshBuffer(); - *buffer = *(getMeshBuffers()[i]); - } - - for (u32 j = 0; j < getAllJoints().size(); ++j) - { - ISkinnedMesh::SJoint *joint = skinned_mesh->addJoint(); - *joint = *(getAllJoints()[j]); - } - - // fix children pointers (they still have old pointers) - core::array & new_joints = skinned_mesh->getAllJoints(); - for (u32 i = 0; i < new_joints.size(); ++i) - { - ISkinnedMesh::SJoint * joint = new_joints[i]; - for (u32 c = 0; c < joint->Children.size(); ++c) - { - // the child is one of the oldJoints and must be replaced by the newjoint on the same index - bool found = false; - for (u32 k = 0; k < AllJoints.size(); ++k) - { - if (joint->Children[c] == AllJoints[k]) - { - joint->Children[c] = new_joints[k]; - found = true; - break; - } - } // k < old_joints.size - - if (!found) - found = true; - } // c < joint->Children.size() - } // i < new_joints.size() - - // In finalize the values from LocalBuffers are copied into - // Weights[].StaticPos. Since skinned_mesh already has the correct - // values in Weights, we have to copy the values from Weights - // into LocalBuffer (so that in the copy from LocalBuffer to weights - // no values are overwritten). - // FIXME: Not ideal, better would be not to copy the values in - // finalize(). - for (unsigned int i = 0; iWeights.size(); ++j) - { - const u16 buffer_id = joint->Weights[j].buffer_id; - const u32 vertex_id = joint->Weights[j].vertex_id; - - skinned_mesh->LocalBuffers[buffer_id]->getVertex(vertex_id)->Pos = joint->Weights[j].StaticPos; - skinned_mesh->LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal = joint->Weights[j].StaticNormal; - } - } - skinned_mesh->finalize(); - - - - return skinned_mesh; - -} // clone - } // end namespace scene } // end namespace irr diff --git a/lib/irrlicht/source/Irrlicht/CSkinnedMesh.h b/lib/irrlicht/source/Irrlicht/CSkinnedMesh.h index 2946d14c2..2c66bbf9b 100644 --- a/lib/irrlicht/source/Irrlicht/CSkinnedMesh.h +++ b/lib/irrlicht/source/Irrlicht/CSkinnedMesh.h @@ -159,7 +159,6 @@ namespace scene void addJoints(core::array &jointChildSceneNodes, IAnimatedMeshSceneNode* node, ISceneManager* smgr); - CSkinnedMesh *clone(); private: void checkForAnimation(); diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 51cadb2f3..994a0015c 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -1069,28 +1069,6 @@ scene::IMesh *IrrDriver::getMesh(const std::string &filename) return am->getMesh(0); } // getMesh -// ---------------------------------------------------------------------------- -/** Create a skinned mesh which has copied all meshbuffers and joints of the - * original mesh. Note, that this will not copy any other information like - * joints data. - * \param mesh Original mesh - * \return Newly created skinned mesh. You should call drop() when you don't - * need it anymore. - */ -scene::IAnimatedMesh *IrrDriver::copyAnimatedMesh(scene::IAnimatedMesh *orig) -{ - using namespace scene; - CSkinnedMesh *mesh = dynamic_cast(orig); - if (!mesh) - { - Log::error("copyAnimatedMesh", "Given mesh was not a skinned mesh."); - return NULL; - } - - scene::IAnimatedMesh* out = mesh->clone(); - return out; -} // copyAnimatedMesh - // ---------------------------------------------------------------------------- /** Sets the material flags in this mesh depending on the settings in * material_manager. diff --git a/src/graphics/irr_driver.hpp b/src/graphics/irr_driver.hpp index 3f78657b5..d7fa11a75 100644 --- a/src/graphics/irr_driver.hpp +++ b/src/graphics/irr_driver.hpp @@ -358,7 +358,6 @@ public: void setAllMaterialFlags(scene::IMesh *mesh) const; scene::IAnimatedMesh *getAnimatedMesh(const std::string &name); scene::IMesh *getMesh(const std::string &name); - scene::IAnimatedMesh *copyAnimatedMesh(scene::IAnimatedMesh *orig); video::ITexture *applyMask(video::ITexture* texture, const std::string& mask_path); void displayFPS(); From fda3afad936bb78ee11ac7738084ecd1374593b3 Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 27 Oct 2016 21:33:42 +1100 Subject: [PATCH 350/350] Fix 2658 by separating networking code from main loop. --- src/main_loop.cpp | 11 +---- src/main_loop.hpp | 3 ++ src/modes/world_status.cpp | 40 ++++++++++++------- src/modes/world_status.hpp | 5 +++ .../protocols/game_events_protocol.cpp | 9 +++++ .../protocols/game_events_protocol.hpp | 2 +- 6 files changed, 44 insertions(+), 26 deletions(-) diff --git a/src/main_loop.cpp b/src/main_loop.cpp index 2db85e3e5..2a3d103cd 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -34,7 +34,6 @@ #include "network/protocol_manager.hpp" #include "network/race_event_manager.hpp" #include "network/stk_host.hpp" -#include "network/protocols/synchronization_protocol.hpp" #include "online/request_manager.hpp" #include "race/history.hpp" #include "race/race_manager.hpp" @@ -291,15 +290,7 @@ void MainLoop::run() if (World::getWorld() ) { - // In case of networking world we can only start the timing once the - // SynchronizationProtocol has disappeared (which indicates that all - // other protocols necessary for running a game are running). - SynchronizationProtocol* protocol = static_cast( - ProtocolManager::getInstance()->getProtocol(PROTOCOL_SYNCHRONIZATION)); - if (!protocol) - { - World::getWorld()->updateTime(dt); - } + World::getWorld()->updateTime(dt); } PROFILER_POP_CPU_MARKER(); diff --git a/src/main_loop.hpp b/src/main_loop.hpp index eff1f520f..48d215546 100644 --- a/src/main_loop.hpp +++ b/src/main_loop.hpp @@ -28,7 +28,10 @@ typedef unsigned long Uint32; class MainLoop { private: + /** True if the main loop should exit. */ bool m_abort; + + /** True if the frame rate should be throttled. */ bool m_throttle_fps; Uint32 m_curr_time; diff --git a/src/modes/world_status.cpp b/src/modes/world_status.cpp index 9a5f78304..7dcfa8f54 100644 --- a/src/modes/world_status.cpp +++ b/src/modes/world_status.cpp @@ -17,6 +17,7 @@ #include "modes/world_status.hpp" +#include "main_loop.hpp" #include "audio/music_manager.hpp" #include "audio/sfx_base.hpp" #include "audio/sfx_manager.hpp" @@ -32,21 +33,6 @@ #include -//----------------------------------------------------------------------------- -/** Starts the kart engines. - */ -void WorldStatus::startEngines() -{ - if (m_engines_started) - return; - - m_engines_started = true; - for (unsigned int i = 0; i < World::getWorld()->getNumKarts(); i++) - { - World::getWorld()->getKart(i)->startEngineSFX(); - } -} - //----------------------------------------------------------------------------- WorldStatus::WorldStatus() { @@ -65,6 +51,7 @@ WorldStatus::WorldStatus() if (device->getTimer()->isStopped()) device->getTimer()->start(); + m_ready_to_race = false; } // WorldStatus //----------------------------------------------------------------------------- @@ -105,6 +92,10 @@ void WorldStatus::reset() // Set the right music World::getWorld()->getTrack()->startMusic(); + // In case of a networked race the race can only start once + // all protocols are up. This flag waits for that, and is + // set by + m_ready_to_race = !NetworkConfig::get()->isNetworking(); } // reset //----------------------------------------------------------------------------- @@ -121,6 +112,21 @@ WorldStatus::~WorldStatus() device->getTimer()->start(); } // ~WorldStatus +//----------------------------------------------------------------------------- +/** Starts the kart engines. + */ +void WorldStatus::startEngines() +{ + if (m_engines_started) + return; + + m_engines_started = true; + for (unsigned int i = 0; i < World::getWorld()->getNumKarts(); i++) + { + World::getWorld()->getKart(i)->startEngineSFX(); + } +} // startEngines + //----------------------------------------------------------------------------- /** Sets the clock mode and the initial time of the world clock. * \param mode The new clock mode. @@ -173,6 +179,10 @@ void WorldStatus::update(float dt) */ void WorldStatus::updateTime(const float dt) { + // In case of a networked race wait till all necessary protocols are + // ready before progressing the timer + if (!m_ready_to_race) return; + switch (m_phase) { // Note: setup phase must be a separate phase, since the race_manager diff --git a/src/modes/world_status.hpp b/src/modes/world_status.hpp index eb821d7c8..d94ede6c1 100644 --- a/src/modes/world_status.hpp +++ b/src/modes/world_status.hpp @@ -94,6 +94,9 @@ protected: /** If the start race should be played, disabled in cutscenes. */ bool m_play_racestart_sounds; + /** A flag that causes the world to wait in case of a networking race + * till all protocols are up and running. */ + bool m_ready_to_race; private: /** Sound to play at the beginning of a race, during which a * a camera intro of the track can be shown. */ @@ -194,6 +197,8 @@ public: // ------------------------------------------------------------------------ /** Get the time since start regardless of which way the clock counts */ float getTimeSinceStart() const { return m_count_up_timer; } + // ------------------------------------------------------------------------ + void setReadyToRace() { m_ready_to_race = true; } }; // WorldStatus diff --git a/src/network/protocols/game_events_protocol.cpp b/src/network/protocols/game_events_protocol.cpp index 0bcf6a8d9..66f520981 100644 --- a/src/network/protocols/game_events_protocol.cpp +++ b/src/network/protocols/game_events_protocol.cpp @@ -33,6 +33,15 @@ GameEventsProtocol::~GameEventsProtocol() { } // ~GameEventsProtocol +// ---------------------------------------------------------------------------- +/** Once the GameEventsProtocol is ready, signal the world that the timer + * can start. +*/ +void GameEventsProtocol::setup() +{ + World::getWorld()->setReadyToRace(); +} // setup + // ---------------------------------------------------------------------------- bool GameEventsProtocol::notifyEvent(Event* event) { diff --git a/src/network/protocols/game_events_protocol.hpp b/src/network/protocols/game_events_protocol.hpp index c847d5db1..f793d3228 100644 --- a/src/network/protocols/game_events_protocol.hpp +++ b/src/network/protocols/game_events_protocol.hpp @@ -27,7 +27,7 @@ public: void kartFinishedRace(const NetworkString &ns); void startReadySetGo(); void receivedReadySetGo(); - virtual void setup() OVERRIDE {}; + virtual void setup() OVERRIDE; virtual void update(float dt) OVERRIDE {}; virtual void asynchronousUpdate() OVERRIDE{} // ------------------------------------------------------------------------