From a82f36d61d4017b347f9137b74969a7a8110bf9e Mon Sep 17 00:00:00 2001 From: fHachenberg Date: Tue, 20 Sep 2016 14:01:02 +0200 Subject: [PATCH 001/125] Fixes issue #2065 Changes applied: *symbol files added *LabelWidget removed from SkillLevelWidget and replaced by IconButtonWidget Subsequently variables and method containing Label or Text renamed setSize method was rewritten *Unused property "m_label_name" removed from SkillLevelWidget *Substitution label->icon also introduced renamings in KartStatsWidget. For instance, setDisplayText is now named setDisplayIcons --- data/gui/mass.png | Bin 0 -> 21897 bytes data/gui/power.png | Bin 0 -> 23859 bytes data/gui/power.png.license.txt | 3 + data/gui/speed.png | Bin 0 -> 30742 bytes src/guiengine/widgets/kart_stats_widget.cpp | 25 +++--- src/guiengine/widgets/kart_stats_widget.hpp | 10 ++- src/guiengine/widgets/player_kart_widget.cpp | 2 +- src/guiengine/widgets/skill_level_widget.cpp | 86 ++++++++++--------- src/guiengine/widgets/skill_level_widget.hpp | 23 ++--- 9 files changed, 81 insertions(+), 68 deletions(-) create mode 100644 data/gui/mass.png create mode 100644 data/gui/power.png create mode 100644 data/gui/power.png.license.txt create mode 100644 data/gui/speed.png diff --git a/data/gui/mass.png b/data/gui/mass.png new file mode 100644 index 0000000000000000000000000000000000000000..e0c876830fc94a6fa01234bb228dd394fc52d370 GIT binary patch literal 21897 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 Ij#Kpi0Km7LegFUf literal 0 HcmV?d00001 diff --git a/data/gui/power.png b/data/gui/power.png new file mode 100644 index 0000000000000000000000000000000000000000..0be43a276c78046004ee2473ea346334b82199bf GIT binary patch literal 23859 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 literal 0 HcmV?d00001 diff --git a/src/guiengine/widgets/kart_stats_widget.cpp b/src/guiengine/widgets/kart_stats_widget.cpp index de33d5a21..182d8cda6 100644 --- a/src/guiengine/widgets/kart_stats_widget.cpp +++ b/src/guiengine/widgets/kart_stats_widget.cpp @@ -23,6 +23,7 @@ #include "karts/kart_properties_manager.hpp" #include "utils/log.hpp" #include "utils/string_utils.hpp" +#include "io/file_manager.hpp" #include #include @@ -37,7 +38,7 @@ using namespace irr; KartStatsWidget::KartStatsWidget(core::recti area, const int player_id, std::string kart_group, bool multiplayer, - bool display_text) : Widget(WTYPE_DIV) + bool display_icons) : Widget(WTYPE_DIV) { m_title_font = !multiplayer; m_player_id = player_id; @@ -66,15 +67,14 @@ KartStatsWidget::KartStatsWidget(core::recti area, const int player_id, "kart '%s' nor any other kart.", default_kart.c_str()); } - - + for (int i = 0; i < SKILL_COUNT; ++i) { irr::core::recti skillArea(0, 0, 1, 1); SkillLevelWidget* skill_bar = NULL; - skill_bar = new SkillLevelWidget(skillArea, m_player_id, multiplayer, display_text); + skill_bar = new SkillLevelWidget(skillArea, m_player_id, multiplayer, display_icons); m_skills.push_back(skill_bar); m_children.push_back(skill_bar); @@ -86,16 +86,19 @@ KartStatsWidget::KartStatsWidget(core::recti area, const int player_id, // different masses or velocities. m_skills[SKILL_MASS]->setValue((int) ((props->getCombinedCharacteristic()->getMass() - 20) / 4)); - m_skills[SKILL_MASS]->setLabel(_("WEIGHT")); - m_skills[SKILL_MASS]->m_properties[PROP_ID] = StringUtils::insertValues("@p%i_mass", m_player_id); + m_skills[SKILL_MASS]->setIcon(irr::core::stringc( + file_manager->getAsset(FileManager::GUI, "mass.png").c_str())); + m_skills[SKILL_MASS]->m_properties[PROP_ID] = StringUtils::insertValues("@p%i_mass", m_player_id); m_skills[SKILL_SPEED]->setValue((int) ((props->getCombinedCharacteristic()->getEngineMaxSpeed() - 15) * 6)); - m_skills[SKILL_SPEED]->setLabel(_("SPEED")); + m_skills[SKILL_SPEED]->setIcon(irr::core::stringc( + file_manager->getAsset(FileManager::GUI, "speed.png").c_str())); m_skills[SKILL_SPEED]->m_properties[PROP_ID] = StringUtils::insertValues("@p%i_speed", m_player_id); - m_skills[SKILL_POWER]->setValue((int) ((props->getAvgPower() - 30) / 20)); - m_skills[SKILL_POWER]->setLabel(_("POWER")); + m_skills[SKILL_POWER]->setValue((int) ((props->getAvgPower() - 30) / 20)); + m_skills[SKILL_POWER]->setIcon(irr::core::stringc( + file_manager->getAsset(FileManager::GUI, "power.png").c_str())); m_skills[SKILL_POWER]->m_properties[PROP_ID] = StringUtils::insertValues("@p%i_power", m_player_id); move(area.UpperLeftCorner.X, area.UpperLeftCorner.Y, @@ -167,11 +170,11 @@ void KartStatsWidget::setSize(const int x, const int y, const int w, const int h m_skill_bar_y = y + h/2 - m_skill_bar_h/2; } // setSize -void KartStatsWidget::setDisplayText(bool display_text) +void KartStatsWidget::setDisplayIcons(bool display_icons) { for (int i = 0; i < SKILL_COUNT; ++i) { - m_skills[i]->setDisplayText(display_text); + m_skills[i]->setDisplayIcon(display_icons); } } // setDisplayText diff --git a/src/guiengine/widgets/kart_stats_widget.hpp b/src/guiengine/widgets/kart_stats_widget.hpp index e78aa4555..96d87fe5f 100644 --- a/src/guiengine/widgets/kart_stats_widget.hpp +++ b/src/guiengine/widgets/kart_stats_widget.hpp @@ -47,7 +47,9 @@ namespace GUIEngine * if/how much space must be added to the raw label's size for the widget to be large enough */ virtual int getHeightNeededAroundLabel() const { return 4; } - /** widget coordinates */ + /** widget coordinates + These are not the actual coordinates of any of the skill bars + but only (badly named) intermediate values*/ int m_skill_bar_x, m_skill_bar_y, m_skill_bar_h, m_skill_bar_w; int m_player_id; @@ -69,7 +71,7 @@ namespace GUIEngine KartStatsWidget(core::recti area, const int player_id, std::string kart_group, bool multiplayer, - bool display_text); + bool display_icons); virtual ~KartStatsWidget() {}; // ------------------------------------------------------------------------ @@ -97,8 +99,8 @@ namespace GUIEngine /** Get the current values of the widget. */ int getValue(Stats type); - /** If the labels should be displayed. */ - void setDisplayText(bool display_text); + /** If the icons should be displayed. */ + void setDisplayIcons(bool display_icons); }; } diff --git a/src/guiengine/widgets/player_kart_widget.cpp b/src/guiengine/widgets/player_kart_widget.cpp index 2af1678a0..d0af7382e 100644 --- a/src/guiengine/widgets/player_kart_widget.cpp +++ b/src/guiengine/widgets/player_kart_widget.cpp @@ -279,7 +279,7 @@ void PlayerKartWidget::setPlayerID(const int newPlayerID) m_player_id = newPlayerID; if (!m_ready) m_player_ident_spinner->setID(m_player_id); - m_kart_stats->setDisplayText(m_player_id == 0); + m_kart_stats->setDisplayIcons(m_player_id == 0); // restore previous focus, but with new player ID if (focus != NULL) focus->setFocusForPlayer(m_player_id); diff --git a/src/guiengine/widgets/skill_level_widget.cpp b/src/guiengine/widgets/skill_level_widget.cpp index d3d8a602d..d422a0af2 100644 --- a/src/guiengine/widgets/skill_level_widget.cpp +++ b/src/guiengine/widgets/skill_level_widget.cpp @@ -26,6 +26,7 @@ #include "utils/string_utils.hpp" #include "config/user_config.hpp" +#include "icon_button_widget.hpp" #include #include @@ -38,12 +39,11 @@ using namespace irr; // ----------------------------------------------------------------------------- SkillLevelWidget::SkillLevelWidget(core::recti area, const int player_id, - bool multiplayer, bool display_text, - const int value, const stringw& label) + bool multiplayer, bool display_icon, + const int value) : Widget(WTYPE_DIV) { m_player_id = player_id; - m_display_text = display_text; setSize(area.UpperLeftCorner.X, area.UpperLeftCorner.Y, area.getWidth(), area.getHeight() ); @@ -60,19 +60,22 @@ SkillLevelWidget::SkillLevelWidget(core::recti area, const int player_id, m_bar->m_h = m_bar_h; m_bar->m_properties[PROP_ID] = StringUtils::insertValues("@p%i_skill_bar", m_player_id); - m_label = NULL; + m_iconbutton = NULL; - m_label = new LabelWidget(!multiplayer, true); - m_label->setText(label,false); + m_iconbutton = new IconButtonWidget(IconButtonWidget::SCALE_MODE_KEEP_TEXTURE_ASPECT_RATIO, + false, false, IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE); - m_label->m_x = m_label_x; - m_label->m_y = m_label_y; - m_label->m_w = m_label_w; - m_label->m_h = m_label_h; - m_label->m_properties[PROP_ID] = StringUtils::insertValues("@p%i_skill_label", m_player_id); + //m_iconbutton_* properties are calculated in setSize method + m_iconbutton->m_x = m_iconbutton_x; + m_iconbutton->m_y = m_iconbutton_y; + m_iconbutton->m_w = m_iconbutton_w; + m_iconbutton->m_h = m_iconbutton_h; + m_iconbutton->m_properties[PROP_ID] = StringUtils::insertValues("@p%i_skill_label", m_player_id); m_children.push_back(m_bar); - m_children.push_back(m_label); + m_children.push_back(m_iconbutton); + + m_display_icon = display_icon; } // KartStatsWidget // ----------------------------------------------------------------------------- @@ -80,8 +83,8 @@ SkillLevelWidget::SkillLevelWidget(core::recti area, const int player_id, void SkillLevelWidget::add() { m_bar->add(); - m_label->add(); - m_label->setVisible(m_display_text); + m_iconbutton->add(); + m_iconbutton->setVisible(m_display_icon); } // ----------------------------------------------------------------------------- @@ -99,12 +102,12 @@ void SkillLevelWidget::move(const int x, const int y, const int w, const int h) m_bar_w, m_bar_h ); } - if (m_label != NULL) + if (m_iconbutton != NULL) { - m_label->move(m_label_x, - m_label_y, - m_label_w, - m_label_h); + m_iconbutton->move( m_iconbutton_x, + m_iconbutton_y, + m_iconbutton_w, + m_iconbutton_h); } } @@ -116,32 +119,30 @@ void SkillLevelWidget::setSize(const int x, const int y, const int w, const int m_y = y; m_w = w; m_h = h; + + int iconbox_h = h; //within icon box, icon is drawn at 75% size + int iconbox_w = h; //assuming square icon + + m_iconbutton_h = iconbox_h * 3 / 4; + m_iconbutton_w = iconbox_w * 3 / 4; // -- sizes - if (m_display_text) - m_bar_w = (w / 2) * 3 / 4; - else - m_bar_w = w * 2 / 3; - m_bar_h = h; - m_label_w = w/2; - m_label_h = h; + m_bar_w = m_w - iconbox_w; //leaving just enough space for icon + its margin + m_bar_h = h; // for shrinking effect if (h < 175) { const float factor = h / 175.0f; m_bar_h = (int)(m_bar_h*factor); - m_label_h = (int)(m_label_h*factor); + // no scale effect for icon (becomes too small otherwise) } + + m_bar_x = x + iconbox_w; + m_bar_y = y + h/2 - m_bar_h/2; //align to midpoint in y direction - if (m_display_text) - m_bar_x = x + w / 2; - else - m_bar_x = x + w / 6; - m_bar_y = y + m_h/2 - m_bar_h/2; - - m_label_x = x; - m_label_y = y + m_h/2 - m_label_h/2; + m_iconbutton_x = x; + m_iconbutton_y = y + h/2 - m_iconbutton_h/2; //align to midpoint in y direction } // setSize // ----------------------------------------------------------------------------- @@ -153,18 +154,19 @@ void SkillLevelWidget::setValue(const int value) // ----------------------------------------------------------------------------- -void SkillLevelWidget::setLabel(const irr::core::stringw& label) +void SkillLevelWidget::setIcon(const irr::core::stringc& filepath) { - m_label->setText(label, false); + m_iconbutton->setImage(filepath.c_str()); } -void SkillLevelWidget::setDisplayText(bool display_text) +// ----------------------------------------------------------------------------- + +void SkillLevelWidget::setDisplayIcon(bool display_icon) { - if(m_display_text != display_text) + if(m_display_icon != display_icon) { - m_display_text = display_text; - m_label->setVisible(display_text); + m_display_icon = display_icon; + m_iconbutton->setVisible(display_icon); setSize(m_x, m_y, m_w, m_h); } } - diff --git a/src/guiengine/widgets/skill_level_widget.hpp b/src/guiengine/widgets/skill_level_widget.hpp index 0fecf8598..00f93a7b7 100644 --- a/src/guiengine/widgets/skill_level_widget.hpp +++ b/src/guiengine/widgets/skill_level_widget.hpp @@ -26,7 +26,7 @@ #include "utils/leak_check.hpp" #include "utils/ptr_vector.hpp" -#include "guiengine/widgets/label_widget.hpp" +#include "guiengine/widgets/icon_button_widget.hpp" #include "guiengine/widgets/progress_bar_widget.hpp" @@ -48,22 +48,22 @@ namespace GUIEngine /** widget coordinates */ int m_bar_x, m_bar_y, m_bar_h, m_bar_w; - int m_label_x, m_label_y, m_label_h, m_label_w; - - std::string m_label_name; + int m_iconbutton_x, m_iconbutton_y, m_iconbutton_h, m_iconbutton_w; int m_player_id; - bool m_display_text; + //shall icon be display left of the skill bar? + bool m_display_icon; public: LEAK_CHECK() - LabelWidget* m_label; + IconButtonWidget* m_iconbutton; ProgressBarWidget* m_bar; - SkillLevelWidget(core::recti area, const int player_id, bool multiplayer, bool display_text, - const int value = 0, const irr::core::stringw& label = "default"); + SkillLevelWidget(core::recti area, const int player_id, + bool multiplayer, bool display_text, + const int value = 0); virtual ~SkillLevelWidget() {}; @@ -89,15 +89,18 @@ namespace GUIEngine /** Change the label of the widget */ void setLabel(const irr::core::stringw& label); + + /** Change the image for the icon. Expects an absolute file path*/ + void setIcon(const irr::core::stringc& filepath); /** Get the current label of the widget. */ const irr::core::stringw getLabel() { - return m_label->getText(); + return m_iconbutton->getText(); } /** If the label should be displayed. */ - void setDisplayText(bool display_text); + void setDisplayIcon(bool display_icon); }; } From 1b75ed9b4cd2c2fade8894e3734896112849b59b Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 12 May 2017 17:11:46 +1000 Subject: [PATCH 002/125] First fix for the basket-ball in cannon - make the ball follow the cannon. --- src/items/flyable.cpp | 28 +++++- src/items/flyable.hpp | 18 +++- src/items/rubber_ball.cpp | 21 +++- src/items/rubber_ball.hpp | 1 + src/karts/abstract_kart_animation.cpp | 39 ++++--- src/karts/cannon_animation.cpp | 140 ++++++++++++++++++-------- src/karts/cannon_animation.hpp | 14 ++- src/tracks/check_cannon.cpp | 61 ++++++++++- src/tracks/check_cannon.hpp | 12 ++- src/tracks/check_cylinder.cpp | 6 +- src/tracks/check_cylinder.hpp | 2 +- src/tracks/check_goal.cpp | 5 +- src/tracks/check_goal.hpp | 2 +- src/tracks/check_lap.cpp | 8 +- src/tracks/check_lap.hpp | 2 +- src/tracks/check_line.cpp | 38 +++---- src/tracks/check_line.hpp | 16 +-- src/tracks/check_manager.cpp | 31 ++++++ src/tracks/check_manager.hpp | 3 + src/tracks/check_sphere.cpp | 4 +- src/tracks/check_sphere.hpp | 2 +- src/tracks/check_structure.hpp | 2 +- 22 files changed, 347 insertions(+), 108 deletions(-) diff --git a/src/items/flyable.cpp b/src/items/flyable.cpp index e0b122844..e18b37f18 100644 --- a/src/items/flyable.cpp +++ b/src/items/flyable.cpp @@ -67,13 +67,14 @@ Flyable::Flyable(AbstractKart *kart, PowerupManager::PowerupType type, m_type = type; m_has_hit_something = false; m_shape = NULL; + m_animation = NULL; m_mass = mass; m_adjust_up_velocity = true; m_time_since_thrown = 0; m_position_offset = Vec3(0,0,0); m_owner_has_temporary_immunity = true; m_do_terrain_info = true; - m_max_lifespan = -1; + m_max_lifespan = -1; // Add the graphical model #ifndef SERVER_ONLY @@ -353,6 +354,24 @@ void Flyable::getLinearKartItemIntersection (const Vec3 &origin, + ( target_y_speed); } // getLinearKartItemIntersection +//----------------------------------------------------------------------------- + +void Flyable::setAnimation(AbstractKartAnimation *animation) +{ + if (animation) + { + assert(m_animation == NULL); + Physics::getInstance()->removeBody(m_body); + } + else // animation = NULL + { + assert(m_animation != NULL); + m_body->setWorldTransform(getTrans()); + Physics::getInstance()->addBody(m_body); + } + m_animation = animation; +} // addAnimation + //----------------------------------------------------------------------------- /** Updates this flyable. It calls Moveable::update. If this function returns * true, the flyable will be deleted by the projectile manager. @@ -361,6 +380,13 @@ void Flyable::getLinearKartItemIntersection (const Vec3 &origin, */ bool Flyable::updateAndDelete(float dt) { + if (hasAnimation()) + { + m_animation->update(dt); + Moveable::update(dt); + return false; + } // if animation + m_time_since_thrown += dt; if(m_max_lifespan > -1 && m_time_since_thrown > m_max_lifespan) hit(NULL); diff --git a/src/items/flyable.hpp b/src/items/flyable.hpp index 8daa0fdb5..3b46b7ae6 100644 --- a/src/items/flyable.hpp +++ b/src/items/flyable.hpp @@ -34,6 +34,7 @@ using namespace irr; #include "tracks/terrain_info.hpp" class AbstractKart; +class AbstractKartAnimation; class HitEffect; class PhysicalObject; class XMLNode; @@ -64,6 +65,11 @@ private: * terrain yourself (e.g. order of operations is important) * set this to false with a call do setDoTerrainInfo(). */ bool m_do_terrain_info; + + /** If the flyable is in a cannon, this is the pointer to the cannon + * animation. NULL otherwise. */ + AbstractKartAnimation *m_animation; + protected: /** Kart which shot this flyable. */ AbstractKart* m_owner; @@ -162,11 +168,16 @@ public: static void init (const XMLNode &node, scene::IMesh *model, PowerupManager::PowerupType type); virtual bool updateAndDelete(float); + virtual void setAnimation(AbstractKartAnimation *animation); virtual HitEffect* getHitEffect() const; bool isOwnerImmunity(const AbstractKart *kart_hit) const; virtual bool hit(AbstractKart* kart, PhysicalObject* obj=NULL); void explode(AbstractKart* kart, PhysicalObject* obj=NULL, bool secondary_hits=true); + unsigned int getOwnerId(); + // ------------------------------------------------------------------------ + /** Returns if this flyable has an animation playing (e.g. cannon). */ + bool hasAnimation() const { return m_animation != NULL; } // ------------------------------------------------------------------------ /** If true the up velocity of the flyable will be adjust so that the * flyable stays at a height close to the average height. @@ -194,15 +205,16 @@ public: void reset () { Moveable::reset(); } // ------------------------------------------------------------------------ /** Returns the type of flyable. */ - PowerupManager::PowerupType - getType() const {return m_type;} + PowerupManager::PowerupType getType() const {return m_type;} // ------------------------------------------------------------------------ /** Sets wether Flyable should update TerrainInfo as part of its update * call, or if the inheriting object will update TerrainInfo itself * (or perhaps not at all if it is not needed). */ void setDoTerrainInfo(bool d) { m_do_terrain_info = d; } // ------------------------------------------------------------------------ - unsigned int getOwnerId(); + /** Returns the size (extend) of the mesh. */ + const Vec3 &getExtend() const { return m_extend; } + // ------------------------------------------------------------------------ }; // Flyable #endif diff --git a/src/items/rubber_ball.cpp b/src/items/rubber_ball.cpp index 2f69e3c43..2b010ac4d 100644 --- a/src/items/rubber_ball.cpp +++ b/src/items/rubber_ball.cpp @@ -29,6 +29,7 @@ #include "modes/linear_world.hpp" #include "physics/btKart.hpp" #include "physics/triangle_mesh.hpp" +#include "tracks/check_manager.hpp" #include "tracks/drive_graph.hpp" #include "tracks/drive_node.hpp" #include "tracks/track.hpp" @@ -103,7 +104,7 @@ RubberBall::RubberBall(AbstractKart *kart) DriveGraph::get()->getNode(getCurrentGraphNode())->getNormal(); TerrainInfo::update(getXYZ(), -normal); initializeControlPoints(m_owner->getXYZ()); - + CheckManager::get()->addFlyableToCannons(this); } // RubberBall // ---------------------------------------------------------------------------- @@ -114,6 +115,7 @@ RubberBall::~RubberBall() if(m_ping_sfx->getStatus()==SFXBase::SFX_PLAYING) m_ping_sfx->stop(); m_ping_sfx->deleteSFX(); + CheckManager::get()->removeFlyableFromCannons(this); } // ~RubberBall // ---------------------------------------------------------------------------- @@ -145,6 +147,14 @@ void RubberBall::initializeControlPoints(const Vec3 &xyz) m_t_increase = m_speed/m_length_cp_1_2; } // initializeControlPoints +// ---------------------------------------------------------------------------- +void RubberBall::setAnimation(AbstractKartAnimation *animation) +{ + if (!animation) + initializeControlPoints(getXYZ()); + Flyable::setAnimation(animation); +} // setAnimation + // ---------------------------------------------------------------------------- /** Determines the first kart that is still in the race. */ @@ -310,6 +320,7 @@ bool RubberBall::updateAndDelete(float dt) // FIXME: what does the rubber ball do in case of battle mode?? if(!world) return true; + if(m_delete_timer>0) { m_delete_timer -= dt; @@ -323,6 +334,14 @@ bool RubberBall::updateAndDelete(float dt) } } + if (hasAnimation()) + { + // Flyable will call update() of the animation to + // update the ball's position. + m_previous_xyz = getXYZ(); + return Flyable::updateAndDelete(dt); + } + // Update the target in case that the first kart was overtaken (or has // finished the race). computeTarget(); diff --git a/src/items/rubber_ball.hpp b/src/items/rubber_ball.hpp index 2d354136b..5575786e7 100644 --- a/src/items/rubber_ball.hpp +++ b/src/items/rubber_ball.hpp @@ -204,6 +204,7 @@ public: static void init(const XMLNode &node, scene::IMesh *rubberball); virtual bool updateAndDelete(float dt); virtual bool hit(AbstractKart* kart, PhysicalObject* obj=NULL); + virtual void setAnimation(AbstractKartAnimation *animation); static float getTimeBetweenRubberBalls() {return m_time_between_balls;} // ------------------------------------------------------------------------ /** This object does not create an explosion, all affects on diff --git a/src/karts/abstract_kart_animation.cpp b/src/karts/abstract_kart_animation.cpp index 54cc9b09c..f78fdeeba 100644 --- a/src/karts/abstract_kart_animation.cpp +++ b/src/karts/abstract_kart_animation.cpp @@ -24,6 +24,11 @@ #include "karts/skidding.hpp" #include "physics/physics.hpp" +/** Constructor. Note that kart can be NULL in case that the animation is + * used for a basket ball in a cannon animation. + * \param kart Pointer to the kart that is animated, or NULL if the + * the animation is meant for a basket ball etc. + */ AbstractKartAnimation::AbstractKartAnimation(AbstractKart *kart, const std::string &name) { @@ -37,7 +42,7 @@ AbstractKartAnimation::AbstractKartAnimation(AbstractKart *kart, // up animations) if this should happen. In debug mode this condition // is caught by setKartAnimation(), and useful error messages are // printed - if (kart->getKartAnimation()) + if (kart && kart->getKartAnimation()) { AbstractKartAnimation* ka = kart->getKartAnimation(); kart->setKartAnimation(NULL); @@ -46,21 +51,23 @@ AbstractKartAnimation::AbstractKartAnimation(AbstractKart *kart, #endif // Register this animation with the kart (which will free it // later). - kart->setKartAnimation(this); - Physics::getInstance()->removeKart(m_kart); - kart->getSkidding()->reset(); - kart->getSlipstream()->reset(); - if(kart->isSquashed()) + if (kart) { - // A time of 0 reset the squashing - kart->setSquash(0.0f, 0.0f); + kart->setKartAnimation(this); + Physics::getInstance()->removeKart(m_kart); + kart->getSkidding()->reset(); + kart->getSlipstream()->reset(); + if (kart->isSquashed()) + { + // A time of 0 reset the squashing + kart->setSquash(0.0f, 0.0f); + } + + // Reset the wheels (and any other animation played for that kart) + // This avoid the effect that some wheels might be way below the kart + // which is very obvious in the rescue animation. + m_kart->getKartModel()->resetVisualWheelPosition(); } - - // Reset the wheels (and any other animation played for that kart) - // This avoid the effect that some wheels might be way below the kart - // which is very obvious in the rescue animation. - m_kart->getKartModel()->resetVisualWheelPosition(); - } // AbstractKartAnimation // ---------------------------------------------------------------------------- @@ -70,7 +77,7 @@ AbstractKartAnimation::~AbstractKartAnimation() // is deleted (at the end of a race), which means that // world is in the process of being deleted. In this case // we can't call getPhysics() anymore. - if(m_timer < 0) + if(m_timer < 0 && m_kart) { m_kart->getBody()->setAngularVelocity(btVector3(0,0,0)); Physics::getInstance()->addKart(m_kart); @@ -91,7 +98,7 @@ void AbstractKartAnimation::update(float dt) m_timer -= dt; if(m_timer<0) { - m_kart->setKartAnimation(NULL); + if(m_kart) m_kart->setKartAnimation(NULL); delete this; } } // update diff --git a/src/karts/cannon_animation.cpp b/src/karts/cannon_animation.cpp index cd3caced9..60ddbbb94 100644 --- a/src/karts/cannon_animation.cpp +++ b/src/karts/cannon_animation.cpp @@ -21,6 +21,7 @@ #include "animations/animation_base.hpp" #include "animations/ipo.hpp" #include "animations/three_d_animation.hpp" +#include "items/flyable.hpp" #include "karts/abstract_kart.hpp" #include "karts/kart_properties.hpp" #include "modes/world.hpp" @@ -28,7 +29,8 @@ #include "LinearMath/btTransform.h" /** The constructor for the cannon animation. - * \param kart The kart to be animated. + * \param kart The kart to be animated. Can also be NULL if a basket ball + * etc is animated (e.g. cannon animation). * \param ipo The IPO (blender interpolation curve) which the kart * should follow. * \param start_left, start_right: Left and right end points of the line @@ -45,9 +47,41 @@ CannonAnimation::CannonAnimation(AbstractKart *kart, Ipo *ipo, float skid_rot) : AbstractKartAnimation(kart, "CannonAnimation") { - m_curve = new AnimationBase(ipo); - m_timer = ipo->getEndTime(); - + m_flyable = NULL; + init(ipo, start_left, start_right, end_left, end_right, skid_rot); +} // CannonAnimation + +// ---------------------------------------------------------------------------- +/** Constructor for a flyable. It sets the kart data to NULL. + */ +CannonAnimation::CannonAnimation(Flyable *flyable, Ipo *ipo, + const Vec3 &start_left, const Vec3 &start_right, + const Vec3 &end_left, const Vec3 &end_right ) + : AbstractKartAnimation(NULL, "CannonAnimation") +{ + m_flyable = flyable; + init(ipo, start_left, start_right, end_left, end_right, /*skid_rot*/0); +} // CannonAnimation(Flyable*...) + +// ---------------------------------------------------------------------------- +/** Common initialisation for kart-based and flyable-based animations. + * \param ipo The IPO (blender interpolation curve) which the kart + * should follow. + * \param start_left, start_right: Left and right end points of the line + * that the kart just crossed. + * \param end_left, end_right: Left and right end points of the line at + * which the kart finishes. + * \param skid_rot Visual rotation of the kart due to skidding (while this + * value can be queried, the AbstractkartAnimation constructor + * resets the value to 0, so it needs to be passed in. + */ +void CannonAnimation::init(Ipo *ipo, const Vec3 &start_left, + const Vec3 &start_right, const Vec3 &end_left, + const Vec3 &end_right, float skid_rot) +{ + m_curve = new AnimationBase(ipo); + m_timer = ipo->getEndTime(); + // First make sure that left and right points are indeed correct // ------------------------------------------------------------- Vec3 my_start_left = start_left; @@ -57,12 +91,17 @@ CannonAnimation::CannonAnimation(AbstractKart *kart, Ipo *ipo, // (the curve's origin must be in the middle of the line. m_curve->getAt(0, &p0); m_curve->getAt(0.1f, &p1); - Vec3 p2 = 0.5f*(p0 + p1) + m_kart->getNormal(); + Vec3 p2; + if (m_kart) + p2 = 0.5f*(p0 + p1) + m_kart->getNormal(); + else + p2 = 0.5f*(p0 + p1) + m_flyable->getNormal(); + if (start_left.sideofPlane(p0, p1, p2) < 0) { // Left and right start line needs to be swapped my_start_left = start_right; - my_start_right = start_left; + my_start_right = start_left; } // First adjust start and end points to take on each side half the kart @@ -70,8 +109,9 @@ CannonAnimation::CannonAnimation(AbstractKart *kart, Ipo *ipo, Vec3 direction = my_start_right - my_start_left; direction.normalize(); - float kw = m_kart->getKartModel()->getWidth(); - Vec3 adj_start_left = my_start_left + (0.5f*kw) * direction; + float kw = m_kart ? m_kart->getKartModel()->getWidth() + : m_flyable->getExtend().getX(); + Vec3 adj_start_left = my_start_left + (0.5f*kw) * direction; Vec3 adj_start_right = my_start_right - (0.5f*kw) * direction; // Store the length of the start and end line, which is used @@ -99,16 +139,17 @@ CannonAnimation::CannonAnimation(AbstractKart *kart, Ipo *ipo, // This delta is rotated with the kart and added to the interpolated curve // position to get the actual kart position during the animation. Vec3 curve_xyz; + Vec3 xyz = m_kart ? m_kart->getXYZ() : m_flyable->getXYZ(); m_curve->update(0, &curve_xyz); - m_delta = kart->getXYZ() - curve_xyz; - + m_delta = xyz - curve_xyz; + // Compute on which fraction of the start line the kart is, to get the // second component of the kart position: distance along start line Vec3 v = adj_start_left - adj_start_right; float l = v.length(); v /= l; - float f = v.dot(adj_start_left - kart->getXYZ()); + float f = v.dot(adj_start_left - xyz); if (f <= 0) f = 0; else if (f >= l) @@ -138,28 +179,36 @@ CannonAnimation::CannonAnimation(AbstractKart *kart, Ipo *ipo, Vec3 tangent; m_curve->getDerivativeAt(0, &tangent); // Get the current kart orientation - Vec3 forward = m_kart->getTrans().getBasis().getColumn(2); + const btTransform &trans = m_kart ? m_kart->getTrans() + : m_flyable->getBody()->getWorldTransform(); + Vec3 forward = trans.getBasis().getColumn(2); Vec3 v1(tangent), v2(forward); v1.setY(0); v2.setY(0); - m_delta_heading = shortestArcQuatNormalize2(v1, v2) - * btQuaternion(Vec3(0,1,0), skid_rot); + m_delta_heading = shortestArcQuatNormalize2(v1, v2) + * btQuaternion(Vec3(0, 1, 0), skid_rot); // The previous call to m_curve->update will set the internal timer // of the curve to dt. Reset it to 0 to make sure the timer is in // synch with the timer of the CanonAnimation m_curve->reset(); -} // CannonAnimation +} // init // ---------------------------------------------------------------------------- CannonAnimation::~CannonAnimation() { delete m_curve; - - btTransform pos = m_kart->getTrans(); - m_kart->getBody()->setCenterOfMassTransform(pos); - Vec3 v(0, 0, m_kart->getKartProperties()->getEngineMaxSpeed()); - m_kart->setVelocity(pos.getBasis()*v); + if (m_kart) + { + btTransform pos = m_kart->getTrans(); + m_kart->getBody()->setCenterOfMassTransform(pos); + Vec3 v(0, 0, m_kart->getKartProperties()->getEngineMaxSpeed()); + m_kart->setVelocity(pos.getBasis()*v); + } + else + { + m_flyable->setAnimation(NULL); + } } // ~CannonAnimation // ---------------------------------------------------------------------------- @@ -184,7 +233,9 @@ void CannonAnimation::update(float dt) m_curve->getDerivativeAt(m_curve->getAnimationDuration() - m_timer, &tangent); // Get the current kart orientation - Vec3 forward = m_kart->getTrans().getBasis().getColumn(2); + const btTransform &trans = m_kart ? m_kart->getTrans() + : m_flyable->getBody()->getWorldTransform(); + Vec3 forward = trans.getBasis().getColumn(2); // Heading // ------- @@ -200,10 +251,14 @@ void CannonAnimation::update(float dt) // ------------------ // While start and end line have to have the same 'up' vector, karts can // sometimes be not parallel to them. So slowly adjust this over time - Vec3 up = m_kart->getTrans().getBasis().getColumn(1); + Vec3 up = trans.getBasis().getColumn(1); up.normalize(); - Vec3 gravity = -m_kart->getBody()->getGravity(); - gravity.normalize(); + Vec3 gravity = m_kart ? -m_kart->getBody()->getGravity() + : -m_flyable->getBody()->getGravity(); + if (gravity.length2() > 0) + gravity.normalize(); + else + gravity.setValue(0, -1, 0); // Adjust only 5% towards the real up vector. This will smoothly // adjust the kart while the kart is in the air Vec3 target_up_vector = (gravity*0.05f + up*0.95f).normalize(); @@ -220,27 +275,25 @@ void CannonAnimation::update(float dt) // The timer counts backwards, so the fraction goes from 1 to 0 float f = m_timer / m_curve->getAnimationDuration(); - - btQuaternion zero(gravity, 0); - btQuaternion current_delta_heading = zero.slerp(m_delta_heading, f); - - btQuaternion all_heading = m_kart->getRotation()*current_delta_heading*heading; - - m_kart->setRotation(q_up * all_heading); - - - // Then compute the new location of the kart - // ----------------------------------------- float f_current_width = m_start_line_length * f + m_end_line_length * (1.0f - f); - // Adjust the horizontal location based on steering - m_fraction_of_line += m_kart->getSteerPercent()*dt*2.0f; - btClamp(m_fraction_of_line, -1.0f, 1.0f); + // Then compute the new location of the kart + // ----------------------------------------- + btQuaternion all_heading; + if (m_kart) + { + btQuaternion zero(gravity, 0); + btQuaternion current_delta_heading = zero.slerp(m_delta_heading, f); + all_heading = m_kart->getRotation()*current_delta_heading*heading; + m_kart->setRotation(q_up * all_heading); - // horiz_delta is in kart coordinates, the rotation by q will - // transform it to the global coordinate system - Vec3 horiz_delta = Vec3(0.5f*m_fraction_of_line * f_current_width, 0, 0); + // Adjust the horizontal location based on steering + m_fraction_of_line += m_kart->getSteerPercent()*dt*2.0f; + btClamp(m_fraction_of_line, -1.0f, 1.0f); + } // if m_kart + else + all_heading.setValue(0, 0, 0, 1); // Determine direction orthogonal to the curve for the sideway movement // of the kart. @@ -251,5 +304,8 @@ void CannonAnimation::update(float dt) Vec3 curve_xyz; m_curve->update(dt, &curve_xyz); - m_kart->setXYZ(curve_xyz+rotated_delta); + if (m_kart) + m_kart->setXYZ(curve_xyz + rotated_delta); + else + m_flyable->setXYZ(curve_xyz + rotated_delta); } // update diff --git a/src/karts/cannon_animation.hpp b/src/karts/cannon_animation.hpp index f36098d32..37853da81 100644 --- a/src/karts/cannon_animation.hpp +++ b/src/karts/cannon_animation.hpp @@ -26,6 +26,7 @@ class AbstractKart; class AnimationBase; +class Flyable; class Ipo; @@ -46,6 +47,10 @@ protected: /** Stores the curve interpolation for the cannon. */ AnimationBase *m_curve; + /** If this animation is used for a flyable (e.g. basket ball) instead + * of a kart, m_flyable is defined and m_kart is NULL. */ + Flyable *m_flyable; + /** Length of the (adjusted, i.e. taking kart width into account) * start line. */ float m_start_line_length; @@ -61,12 +66,19 @@ protected: /** The initial heading of the kart when crossing the line. This is * used to smoothly orient the kart towards the normal of the cuve. */ btQuaternion m_delta_heading; + + void init(Ipo *ipo, const Vec3 &start_left, const Vec3 &start_right, + const Vec3 &end_left, const Vec3 &end_right, float skid_rot); + public: CannonAnimation(AbstractKart *kart, Ipo *ipo, const Vec3 &start_left, const Vec3 &start_right, const Vec3 &end_left, const Vec3 &end_right, float skid_rot); - virtual ~CannonAnimation(); + CannonAnimation(Flyable *flyable, Ipo *ipo, + const Vec3 &start_left, const Vec3 &start_right, + const Vec3 &end_left, const Vec3 &end_right); + virtual ~CannonAnimation(); virtual void update(float dt); }; // CannonAnimation diff --git a/src/tracks/check_cannon.cpp b/src/tracks/check_cannon.cpp index 9daa715b9..1eb76c8dc 100644 --- a/src/tracks/check_cannon.cpp +++ b/src/tracks/check_cannon.cpp @@ -25,6 +25,7 @@ #include "graphics/show_curve.hpp" #include "graphics/stk_tex_manager.hpp" #include "io/xml_node.hpp" +#include "items/flyable.hpp" #include "karts/abstract_kart.hpp" #include "karts/cannon_animation.hpp" #include "karts/skidding.hpp" @@ -88,9 +89,12 @@ CheckCannon::CheckCannon(const XMLNode &node, unsigned int index) : video::SColor(128, 128, 128, 128); } buffer->recalculateBoundingBox(); - buffer->getMaterial().setTexture(0, STKTexManager::getInstance()->getUnicolorTexture(video::SColor(128, 255, 105, 180))); - buffer->getMaterial().setTexture(1, STKTexManager::getInstance()->getUnicolorTexture(video::SColor(0, 0, 0, 0))); - buffer->getMaterial().setTexture(2, STKTexManager::getInstance()->getUnicolorTexture(video::SColor(0, 0, 0, 0))); + buffer->getMaterial().setTexture(0, STKTexManager::getInstance() + ->getUnicolorTexture(video::SColor(128, 255, 105, 180))); + buffer->getMaterial().setTexture(1, STKTexManager::getInstance() + ->getUnicolorTexture(video::SColor(0, 0, 0, 0))); + buffer->getMaterial().setTexture(2, STKTexManager::getInstance() + ->getUnicolorTexture(video::SColor(0, 0, 0, 0))); buffer->getMaterial().BackfaceCulling = false; //mesh->setBoundingBox(buffer->getBoundingBox()); m_debug_target_node = irr_driver->addMesh(mesh, "checkdebug"); @@ -114,6 +118,8 @@ CheckCannon::~CheckCannon() } // ~CheckCannon // ---------------------------------------------------------------------------- +/** Changes the colour of a check cannon depending on state. + */ void CheckCannon::changeDebugColor(bool is_active) { #if defined(DEBUG) && !defined(SERVER_ONLY) @@ -133,6 +139,55 @@ void CheckCannon::changeDebugColor(bool is_active) #endif } // changeDebugColor +// ---------------------------------------------------------------------------- +/** Adds a flyable to be tested for crossing a cannon checkline. + * \param flyable The flyable to be tested. + */ +void CheckCannon::addFlyable(Flyable *flyable) +{ + m_all_flyables.push_back(flyable); + m_flyable_previous_position.push_back(flyable->getXYZ()); +} // addFlyable + +// ---------------------------------------------------------------------------- +/** Removes a flyable from the tests if it crosses a checkline. Used when + * the flyable is removed (e.g. explodes). + */ +void CheckCannon::removeFlyable(Flyable *flyable) +{ + std::vector::iterator i = std::find(m_all_flyables.begin(), + m_all_flyables.end(), + flyable); + assert(i != m_all_flyables.end()); + int index = i - m_all_flyables.begin(); // get the index + m_all_flyables.erase(i); + m_flyable_previous_position.erase(m_flyable_previous_position.begin() + index); +} // removeFlyable + +// ---------------------------------------------------------------------------- +/** Overriden to also check all flyables registered with the cannon. + */ +void CheckCannon::update(float dt) +{ + CheckLine::update(dt); + for (unsigned int i = 0; i < m_all_flyables.size(); i++) + { + setIgnoreHeight(true); + bool triggered = isTriggered(m_flyable_previous_position[i], + m_all_flyables[i]->getXYZ(), + /*kart index - ignore*/ -1 ); + setIgnoreHeight(false); + m_flyable_previous_position[i] = m_all_flyables[i]->getXYZ(); + if(!triggered) continue; + + // Cross the checkline - add the cannon animation + CannonAnimation *animation = + new CannonAnimation(m_all_flyables[i], m_curve->clone(), + getLeftPoint(), getRightPoint(), + m_target_left, m_target_right); + m_all_flyables[i]->setAnimation(animation); + } // for i in all flyables +} // update // ---------------------------------------------------------------------------- /** Called when the check line is triggered. This function creates a cannon * animation object and attaches it to the kart. diff --git a/src/tracks/check_cannon.hpp b/src/tracks/check_cannon.hpp index 0708d944a..dec83aafc 100644 --- a/src/tracks/check_cannon.hpp +++ b/src/tracks/check_cannon.hpp @@ -21,8 +21,10 @@ #include "animations/animation_base.hpp" #include "tracks/check_line.hpp" +#include "utils/cpp2011.hpp" class CheckManager; +class Flyable; class Ipo; class ShowCurve; class XMLNode; @@ -51,12 +53,18 @@ private: /** Used to display debug information about checklines. */ scene::IMeshSceneNode *m_debug_target_node; #endif + std::vector m_all_flyables; + std::vector m_flyable_previous_position; public: CheckCannon(const XMLNode &node, unsigned int index); virtual ~CheckCannon(); - virtual void trigger(unsigned int kart_index); - virtual void changeDebugColor(bool is_active); + virtual void trigger(unsigned int kart_index) OVERRIDE; + virtual void changeDebugColor(bool is_active) OVERRIDE; + virtual void update(float dt) OVERRIDE; + + void addFlyable(Flyable *flyable); + void removeFlyable(Flyable *flyable); }; // CheckLine #endif diff --git a/src/tracks/check_cylinder.cpp b/src/tracks/check_cylinder.cpp index 669d271ae..c3ad9f35a 100644 --- a/src/tracks/check_cylinder.cpp +++ b/src/tracks/check_cylinder.cpp @@ -46,16 +46,16 @@ CheckCylinder::CheckCylinder(const XMLNode &node, unsigned int index, TriggerIte } // CheckCylinder // ---------------------------------------------------------------------------- -/** True if going from old_pos to new_pos enters or leaves this sphere. This +/** True if going from old_pos to new_pos enters or leaves this cylinder. This * function is called from update (of the checkline structure). It also * updates the flag about which karts are inside * \param old_pos Position in previous frame. * \param new_pos Position in current frame. - * \param kart_id Index of the kart, can be used to store kart specific + * \param kart_id Index of the kart, can be used to store kart specific * additional data. */ bool CheckCylinder::isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, - unsigned int kart_id) + int kart_id) { // TODO: this is the code for a sphere, rewrite for cylinder Vec3 old_pos_xz(old_pos.x(), 0.0f, old_pos.z()); diff --git a/src/tracks/check_cylinder.hpp b/src/tracks/check_cylinder.hpp index 4af674561..6c6f9802a 100644 --- a/src/tracks/check_cylinder.hpp +++ b/src/tracks/check_cylinder.hpp @@ -52,7 +52,7 @@ public: TriggerItemListener* listener); virtual ~CheckCylinder() {}; virtual bool isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, - unsigned int kart_id); + int kart_id); // ------------------------------------------------------------------------ /** Returns if kart indx is currently inside of the sphere. */ bool isInside(int index) const { return m_is_inside[index]; } diff --git a/src/tracks/check_goal.cpp b/src/tracks/check_goal.cpp index 496048f41..538f18916 100644 --- a/src/tracks/check_goal.cpp +++ b/src/tracks/check_goal.cpp @@ -55,7 +55,8 @@ void CheckGoal::update(float dt) if (world) { - if (isTriggered(m_previous_ball_position, world->getBallPosition(), -1)) + if (isTriggered(m_previous_ball_position, world->getBallPosition(), + /*kart index - ignore*/-1) ) { if (UserConfigParams::m_check_debug) { @@ -86,7 +87,7 @@ void CheckGoal::trigger(unsigned int i) // ---------------------------------------------------------------------------- bool CheckGoal::isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, - unsigned int kartIndex) + int kart_index) { core::vector2df cross_point; diff --git a/src/tracks/check_goal.hpp b/src/tracks/check_goal.hpp index d1c36cb7c..a310ca60c 100644 --- a/src/tracks/check_goal.hpp +++ b/src/tracks/check_goal.hpp @@ -65,7 +65,7 @@ public: virtual void update(float dt) OVERRIDE; virtual void trigger(unsigned int kart_index) OVERRIDE; virtual bool isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, - unsigned int indx) OVERRIDE; + int indx) OVERRIDE; virtual void reset(const Track &track) OVERRIDE; // ------------------------------------------------------------------------ diff --git a/src/tracks/check_lap.cpp b/src/tracks/check_lap.cpp index ab2d4bb1c..ee0f8ce8a 100644 --- a/src/tracks/check_lap.cpp +++ b/src/tracks/check_lap.cpp @@ -53,13 +53,13 @@ void CheckLap::reset(const Track &track) // ---------------------------------------------------------------------------- /** True if going from old_pos to new_pos crosses this checkline. This function * is called from update (of the checkline structure). - * \param old_pos Position in previous frame. - * \param new_pos Position in current frame. + * \param old_pos Position in previous frame. + * \param new_pos Position in current frame. * \param kart_index Index of the kart, can be used to store kart specific - * additional data. + * additional data. */ bool CheckLap::isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, - unsigned int kart_index) + int kart_index) { World* w = World::getWorld(); LinearWorld* lin_world = dynamic_cast(w); diff --git a/src/tracks/check_lap.hpp b/src/tracks/check_lap.hpp index b6d19a78a..d8b6f82f3 100644 --- a/src/tracks/check_lap.hpp +++ b/src/tracks/check_lap.hpp @@ -40,7 +40,7 @@ public: CheckLap(const XMLNode &node, unsigned int index); virtual ~CheckLap() {}; virtual bool isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, - unsigned int indx); + int indx); virtual void reset(const Track &track); }; // CheckLine diff --git a/src/tracks/check_line.cpp b/src/tracks/check_line.cpp index bc817dc53..197b586b0 100644 --- a/src/tracks/check_line.cpp +++ b/src/tracks/check_line.cpp @@ -40,6 +40,7 @@ CheckLine::CheckLine(const XMLNode &node, unsigned int index) : CheckStructure(node, index) { + m_ignore_height = false; // Note that when this is called the karts have not been allocated // in world, so we can't call world->getNumKarts() m_previous_sign.resize(race_manager->getNumberOfKarts()); @@ -169,13 +170,14 @@ void CheckLine::changeDebugColor(bool is_active) // ---------------------------------------------------------------------------- /** True if going from old_pos to new_pos crosses this checkline. This function * is called from update (of the checkline structure). - * \param old_pos Position in previous frame. - * \param new_pos Position in current frame. - * \param indx Index of the kart, can be used to store kart specific - * additional data. + * \param old_pos Position in previous frame. + * \param new_pos Position in current frame. + * \param kart_indx Index of the kart, can be used to store kart specific + * additional data. If set to a negative number it will + * be ignored (used for e.g. soccer ball, and basket ball). */ bool CheckLine::isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, - unsigned int kart_index) + int kart_index) { World* w = World::getWorld(); core::vector2df p=new_pos.toIrrVector2d(); @@ -184,7 +186,7 @@ bool CheckLine::isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, bool previous_sign; - if (kart_index == UINT_MAX) + if (kart_index < 0) { core::vector2df p = old_pos.toIrrVector2d(); previous_sign = (m_line.getPointOrientation(p) >= 0); @@ -196,18 +198,20 @@ bool CheckLine::isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, // If the sign has changed, i.e. the infinite line was crossed somewhere, // check if the finite line was actually crossed: + core::vector2df cross_point; if (sign != previous_sign && m_line.intersectWith(core::line2df(old_pos.toIrrVector2d(), new_pos.toIrrVector2d()), - m_cross_point) ) + cross_point) ) { // Now check the minimum height: the kart position must be within a // reasonable distance in the Z axis - 'reasonable' for now to be // between -1 and 4 units (negative numbers are unlikely, but help // in case that the kart is 'somewhat' inside of the track, or the // checklines are a bit off in Z direction. - result = new_pos.getY()-m_min_height-m_under_min_height; + result = m_ignore_height || + (new_pos.getY()-m_min_height-m_under_min_height ); if(UserConfigParams::m_check_debug && !result) { if(World::getWorld()->getNumKarts()>0) @@ -219,20 +223,20 @@ bool CheckLine::isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, Log::info("CheckLine", "Kart %d crosses line, but wrong height " "(%f vs %f).", kart_index, new_pos.getY(), m_min_height); - } } else result = false; - if (kart_index != UINT_MAX) - m_previous_sign[kart_index] = sign; - - if (result && kart_index != UINT_MAX) + if (kart_index >= 0) { - LinearWorld* lw = dynamic_cast(w); - if (lw != NULL) - lw->setLastTriggeredCheckline(kart_index, m_index); + m_previous_sign[kart_index] = sign; + if (result) + { + LinearWorld* lw = dynamic_cast(w); + if (lw != NULL) + lw->setLastTriggeredCheckline(kart_index, m_index); + } } return result; } // isTriggered diff --git a/src/tracks/check_line.hpp b/src/tracks/check_line.hpp index 3b06e2ca8..8eee967af 100644 --- a/src/tracks/check_line.hpp +++ b/src/tracks/check_line.hpp @@ -46,7 +46,10 @@ private: /** The line that is tested for being crossed. */ core::line2df m_line; - core::vector2df m_cross_point; + /** True if this line should ignore the height test. This is required + * e.g. for basketball cannons, since the ball can be too height to + * otherwise trigger the cannon. */ + bool m_ignore_height; /** The minimum height of the checkline. */ float m_min_height; @@ -79,17 +82,18 @@ public: CheckLine(const XMLNode &node, unsigned int index); virtual ~CheckLine(); virtual bool isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, - unsigned int indx); + int indx); virtual void reset(const Track &track); virtual void resetAfterKartMove(unsigned int kart_index); virtual void changeDebugColor(bool is_active); + // ------------------------------------------------------------------------ /** Returns the actual line data for this checkpoint. */ const core::line2df &getLine2D() const {return m_line;} // ------------------------------------------------------------------------ - /** Returns the 2d point at which the line was crossed. Note that this - * value is ONLY valid after isTriggered is called and inside of - * trigger(). */ - const core::vector2df &getCrossPoint() const { return m_cross_point; } + /** Sets if this check line should not do a height test for testing + * if a line is crossed. Used for basket calls in cannon (the ball can + * be too heigh to otherwise trigger he cannon). */ + void setIgnoreHeight(bool b) { m_ignore_height = b; } }; // CheckLine #endif diff --git a/src/tracks/check_manager.cpp b/src/tracks/check_manager.cpp index 502a84006..19c9a85c3 100644 --- a/src/tracks/check_manager.cpp +++ b/src/tracks/check_manager.cpp @@ -131,6 +131,37 @@ void CheckManager::resetAfterKartMove(AbstractKart *kart) (*i)->resetAfterKartMove(kart->getWorldKartId()); } // resetAfterKartMove +// ---------------------------------------------------------------------------- +/** Adds a flyable object to be tested against cannons. This will allow + * bowling- and rubber-balls to fly in a cannon. + * \param flyable Pointer to the flyable to be added. + */ +void CheckManager::addFlyableToCannons(Flyable *flyable) +{ + for (unsigned int i = 0; i < m_all_checks.size(); i++) + { + CheckCannon *cc = dynamic_cast(m_all_checks[i]); + if (cc) + cc->addFlyable(flyable); + } +} // addFlyable + +// ---------------------------------------------------------------------------- +/** Removes a flyable from all cannons. Used when this flyable is removed + * (e.g. explodes). + * \param flyable Pointer to the flyable to be removed. + */ +void CheckManager::removeFlyableFromCannons(Flyable *flyable) +{ + for (unsigned int i = 0; i < m_all_checks.size(); i++) + { + CheckCannon *cc = dynamic_cast(m_all_checks[i]); + if (cc) + cc->removeFlyable(flyable); + } + +} // addFlyable + // ---------------------------------------------------------------------------- /** Updates all animations. Called one per time step. * \param dt Time since last call. diff --git a/src/tracks/check_manager.hpp b/src/tracks/check_manager.hpp index 93b97cdf8..88664c110 100644 --- a/src/tracks/check_manager.hpp +++ b/src/tracks/check_manager.hpp @@ -27,6 +27,7 @@ class AbstractKart; class CheckStructure; +class Flyable; class Track; class XMLNode; class Vec3; @@ -46,6 +47,8 @@ private: ~CheckManager(); public: void add(CheckStructure* strct) { m_all_checks.push_back(strct); } + void addFlyableToCannons(Flyable *flyable); + void removeFlyableFromCannons(Flyable *flyable); void load(const XMLNode &node); void update(float dt); void reset(const Track &track); diff --git a/src/tracks/check_sphere.cpp b/src/tracks/check_sphere.cpp index bd07fcf24..2ba2e32ad 100644 --- a/src/tracks/check_sphere.cpp +++ b/src/tracks/check_sphere.cpp @@ -53,11 +53,11 @@ CheckSphere::CheckSphere(const XMLNode &node, unsigned int index) * updates the flag about which karts are inside * \param old_pos Position in previous frame. * \param new_pos Position in current frame. - * \param kart_id Index of the kart, can be used to store kart specific + * \param kart_id Index of the kart, can be used to store kart specific * additional data. */ bool CheckSphere::isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, - unsigned int kart_id) + int kart_id) { float old_dist2 = (old_pos-m_center_point).length2(); float new_dist2 = (new_pos-m_center_point).length2(); diff --git a/src/tracks/check_sphere.hpp b/src/tracks/check_sphere.hpp index 4780ed681..b11e05fd9 100644 --- a/src/tracks/check_sphere.hpp +++ b/src/tracks/check_sphere.hpp @@ -48,7 +48,7 @@ public: CheckSphere(const XMLNode &node, unsigned int index); virtual ~CheckSphere() {}; virtual bool isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, - unsigned int kart_id); + int kart_id); // ------------------------------------------------------------------------ /** Returns if kart indx is currently inside of the sphere. */ bool isInside(int index) const { return m_is_inside[index]; } diff --git a/src/tracks/check_structure.hpp b/src/tracks/check_structure.hpp index 921c7b3bc..bb7e29661 100644 --- a/src/tracks/check_structure.hpp +++ b/src/tracks/check_structure.hpp @@ -115,7 +115,7 @@ public: * additional data. */ virtual bool isTriggered(const Vec3 &old_pos, const Vec3 &new_pos, - unsigned int indx)=0; + int indx)=0; virtual void trigger(unsigned int kart_index); virtual void reset(const Track &track); From 490bef516eac6306d701d9cacca998795b9e0b94 Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 12 May 2017 17:39:53 +1000 Subject: [PATCH 003/125] Fix crash: the ProjectileManager must be cleared before the CheckManager (since a rubber ball will emove itself from all cannon checklines, so a crash happens if the checklines are gone). --- src/modes/world.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modes/world.cpp b/src/modes/world.cpp index a88282077..19f36081b 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -436,6 +436,8 @@ World::~World() irr_driver->onUnloadWorld(); + projectile_manager->cleanup(); + // In case that a race is aborted (e.g. track not found) track is 0. if(Track::getCurrentTrack()) Track::getCurrentTrack()->cleanup(); @@ -486,8 +488,6 @@ World::~World() Camera::removeAllCameras(); - projectile_manager->cleanup(); - // In case that the track is not found, Physics was not instantiated, // but kill handles this correctly. Physics::kill(); From bdbc7fd7281e983fbb061b2fb8d8adbdde65ca0d Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 12 May 2017 17:52:15 +1000 Subject: [PATCH 004/125] Reduce the height of a rubber ball in a cannon over time, so that it starts closer to the ground when released after the cannon. --- src/items/rubber_ball.cpp | 3 +++ src/karts/cannon_animation.cpp | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/src/items/rubber_ball.cpp b/src/items/rubber_ball.cpp index 2b010ac4d..8dd90e353 100644 --- a/src/items/rubber_ball.cpp +++ b/src/items/rubber_ball.cpp @@ -151,7 +151,10 @@ void RubberBall::initializeControlPoints(const Vec3 &xyz) void RubberBall::setAnimation(AbstractKartAnimation *animation) { if (!animation) + { initializeControlPoints(getXYZ()); + m_height_timer = 0; + } Flyable::setAnimation(animation); } // setAnimation diff --git a/src/karts/cannon_animation.cpp b/src/karts/cannon_animation.cpp index 60ddbbb94..577d3bb9f 100644 --- a/src/karts/cannon_animation.cpp +++ b/src/karts/cannon_animation.cpp @@ -293,7 +293,15 @@ void CannonAnimation::update(float dt) btClamp(m_fraction_of_line, -1.0f, 1.0f); } // if m_kart else + { + // If a rubber ball is in this cannon, reduce its height over + // time so that it starts closer to the ground when released + float height = m_delta.getY(); + float radius = m_flyable->getExtend().getY(); + height = (height - radius) * 0.95f + radius; + m_delta.setY(height); all_heading.setValue(0, 0, 0, 1); + } // Determine direction orthogonal to the curve for the sideway movement // of the kart. From 91bad37939b906c8f08b2ee2f23558d89aff179e Mon Sep 17 00:00:00 2001 From: hiker Date: Sat, 13 May 2017 21:38:59 +1000 Subject: [PATCH 005/125] Fixed incorrect position for a rubber ball in a cannon. --- src/karts/cannon_animation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/karts/cannon_animation.cpp b/src/karts/cannon_animation.cpp index 577d3bb9f..68a31f073 100644 --- a/src/karts/cannon_animation.cpp +++ b/src/karts/cannon_animation.cpp @@ -258,7 +258,7 @@ void CannonAnimation::update(float dt) if (gravity.length2() > 0) gravity.normalize(); else - gravity.setValue(0, -1, 0); + gravity.setValue(0, 1, 0); // Adjust only 5% towards the real up vector. This will smoothly // adjust the kart while the kart is in the air Vec3 target_up_vector = (gravity*0.05f + up*0.95f).normalize(); From 65c8cb7ae119363d592d2fa8f73604a592190e6d Mon Sep 17 00:00:00 2001 From: hiker Date: Sun, 30 Jul 2017 23:03:40 +1000 Subject: [PATCH 006/125] Replaced arrays with std::vector (to get better error checking). --- src/input/gamepad_device.cpp | 23 ++++++++--------------- src/input/gamepad_device.hpp | 9 +++++---- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/src/input/gamepad_device.cpp b/src/input/gamepad_device.cpp index da1b546b7..090f005ee 100644 --- a/src/input/gamepad_device.cpp +++ b/src/input/gamepad_device.cpp @@ -33,7 +33,6 @@ GamePadDevice::GamePadDevice(const int irr_index, const std::string &name, GamepadConfig *configuration) { m_type = DT_GAMEPAD; - m_prev_axis_directions = NULL; m_configuration = configuration; GamepadConfig *config = static_cast(m_configuration); if(m_configuration->getNumberOfButtons()setNumberOfAxis(axis_count); } - m_prev_axis_directions = new Input::AxisDirection[axis_count]; - m_prev_axis_value = new int[axis_count]; - m_axis_ok = new bool[axis_count]; + m_prev_axis_directions.resize(axis_count); + m_prev_axis_value.resize(axis_count); + m_axis_ok.resize(axis_count); m_irr_index = irr_index; m_name = name; @@ -57,9 +56,9 @@ GamePadDevice::GamePadDevice(const int irr_index, const std::string &name, m_axis_ok[i] = false; } - m_buttonPressed = new bool[button_count]; + m_button_pressed.resize(button_count); for(int n=0; n class GamepadConfig; /** @@ -31,9 +32,9 @@ class GamepadConfig; class GamePadDevice : public InputDevice { void resetAxisDirection(const int axis, Input::AxisDirection direction); - bool* m_buttonPressed; + std::vector m_button_pressed; - Input::AxisDirection *m_prev_axis_directions; + std::vector m_prev_axis_directions; /** used to determine if an axis is valid; an axis is considered valid * when at least 2 different values are read from this axis (if an axis @@ -43,10 +44,10 @@ class GamePadDevice : public InputDevice * on linux some hard disks may be reported as gamepads with * uninteresting axis values) */ - int *m_prev_axis_value; + std::vector m_prev_axis_value; /** \see m_prev_axis_value */ - bool *m_axis_ok; + std::vector m_axis_ok; /** Irrlicht index of this gamepad. */ int m_irr_index; From e6a134c3479ccdbaf91bb36d46ffb64b6a3b52b9 Mon Sep 17 00:00:00 2001 From: hiker Date: Sun, 30 Jul 2017 23:05:27 +1000 Subject: [PATCH 007/125] Fix 2850 by increasing the number of axis for a gamepad to include D-pad. --- src/input/gamepad_device.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/input/gamepad_device.cpp b/src/input/gamepad_device.cpp index 090f005ee..423c1894f 100644 --- a/src/input/gamepad_device.cpp +++ b/src/input/gamepad_device.cpp @@ -39,13 +39,21 @@ GamePadDevice::GamePadDevice(const int irr_index, const std::string &name, { config->setNumberOfButtons(button_count); } - if(m_configuration->getNumberOfAxes() HAT_H_ID. So increase the number of axis to be large + // enough to handle HAT_V/H_ID as axis number. + assert(Input::HAT_V_ID > Input::HAT_H_ID); + int adj_axis_count = axis_count > Input::HAT_V_ID ? axis_count + : Input::HAT_V_ID+1; + + if(m_configuration->getNumberOfAxes()setNumberOfAxis(axis_count); + config->setNumberOfAxis(adj_axis_count); } - m_prev_axis_directions.resize(axis_count); - m_prev_axis_value.resize(axis_count); - m_axis_ok.resize(axis_count); + m_prev_axis_directions.resize(adj_axis_count); + m_prev_axis_value.resize(adj_axis_count); + m_axis_ok.resize(adj_axis_count); m_irr_index = irr_index; m_name = name; From 85ee217d77ce6272aa44dbd7ac2fa27f7735d58a Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 1 Aug 2017 17:19:52 +1000 Subject: [PATCH 008/125] Converted tab to spaces. --- .../source/Irrlicht/CIrrDeviceWin32.cpp | 3124 ++++++++--------- 1 file changed, 1562 insertions(+), 1562 deletions(-) diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp index 9898aba8d..c17da02b2 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp @@ -39,327 +39,327 @@ namespace irr { - namespace video - { - #ifdef _IRR_COMPILE_WITH_DIRECT3D_8_ - IVideoDriver* createDirectX8Driver(const irr::SIrrlichtCreationParameters& params, - io::IFileSystem* io, HWND window); - #endif + namespace video + { + #ifdef _IRR_COMPILE_WITH_DIRECT3D_8_ + IVideoDriver* createDirectX8Driver(const irr::SIrrlichtCreationParameters& params, + io::IFileSystem* io, HWND window); + #endif - #ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ - IVideoDriver* createDirectX9Driver(const irr::SIrrlichtCreationParameters& params, - io::IFileSystem* io, HWND window); - #endif + #ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ + IVideoDriver* createDirectX9Driver(const irr::SIrrlichtCreationParameters& params, + io::IFileSystem* io, HWND window); + #endif - #ifdef _IRR_COMPILE_WITH_OPENGL_ - IVideoDriver* createOpenGLDriver(const irr::SIrrlichtCreationParameters& params, - io::IFileSystem* io, CIrrDeviceWin32* device); - #endif - } + #ifdef _IRR_COMPILE_WITH_OPENGL_ + IVideoDriver* createOpenGLDriver(const irr::SIrrlichtCreationParameters& params, + io::IFileSystem* io, CIrrDeviceWin32* device); + #endif + } } // end namespace irr namespace irr { struct SJoystickWin32Control { - CIrrDeviceWin32* Device; + CIrrDeviceWin32* Device; #if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) && defined(_IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_) - IDirectInput8* DirectInputDevice; + IDirectInput8* DirectInputDevice; #endif #if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) - struct JoystickInfo - { - u32 Index; + struct JoystickInfo + { + u32 Index; #ifdef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ - core::stringc Name; - GUID guid; - LPDIRECTINPUTDEVICE8 lpdijoy; - DIDEVCAPS devcaps; - u8 axisValid[8]; + core::stringc Name; + GUID guid; + LPDIRECTINPUTDEVICE8 lpdijoy; + DIDEVCAPS devcaps; + u8 axisValid[8]; #else - JOYCAPS Caps; + JOYCAPS Caps; #endif - }; - core::array ActiveJoysticks; + }; + core::array ActiveJoysticks; #endif - SJoystickWin32Control(CIrrDeviceWin32* dev) : Device(dev) - { + SJoystickWin32Control(CIrrDeviceWin32* dev) : Device(dev) + { #if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) && defined(_IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_) - DirectInputDevice=0; - if (DI_OK != (DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&DirectInputDevice, NULL))) - { - os::Printer::log("Could not create DirectInput8 Object", ELL_WARNING); - return; - } + DirectInputDevice=0; + if (DI_OK != (DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&DirectInputDevice, NULL))) + { + os::Printer::log("Could not create DirectInput8 Object", ELL_WARNING); + return; + } #endif - } - ~SJoystickWin32Control() - { + } + ~SJoystickWin32Control() + { #if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) && defined(_IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_) - for(u32 joystick = 0; joystick < ActiveJoysticks.size(); ++joystick) - { - LPDIRECTINPUTDEVICE8 dev = ActiveJoysticks[joystick].lpdijoy; - if (dev) - { - dev->Unacquire(); - } - dev->Release(); - } + for(u32 joystick = 0; joystick < ActiveJoysticks.size(); ++joystick) + { + LPDIRECTINPUTDEVICE8 dev = ActiveJoysticks[joystick].lpdijoy; + if (dev) + { + dev->Unacquire(); + } + dev->Release(); + } - if (DirectInputDevice) - DirectInputDevice->Release(); + if (DirectInputDevice) + DirectInputDevice->Release(); #endif - } + } #if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) && defined(_IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_) - static BOOL CALLBACK EnumJoysticks(LPCDIDEVICEINSTANCE lpddi, LPVOID cp) - { - SJoystickWin32Control* p=(SJoystickWin32Control*)cp; - p->directInputAddJoystick(lpddi); - return DIENUM_CONTINUE; - } - void directInputAddJoystick(LPCDIDEVICEINSTANCE lpddi) - { - //Get the GUID of the joystuck - const GUID guid = lpddi->guidInstance; + static BOOL CALLBACK EnumJoysticks(LPCDIDEVICEINSTANCE lpddi, LPVOID cp) + { + SJoystickWin32Control* p=(SJoystickWin32Control*)cp; + p->directInputAddJoystick(lpddi); + return DIENUM_CONTINUE; + } + void directInputAddJoystick(LPCDIDEVICEINSTANCE lpddi) + { + //Get the GUID of the joystuck + const GUID guid = lpddi->guidInstance; - JoystickInfo activeJoystick; - activeJoystick.Index=ActiveJoysticks.size(); - activeJoystick.guid=guid; - activeJoystick.Name=lpddi->tszProductName; - if (FAILED(DirectInputDevice->CreateDevice(guid, &activeJoystick.lpdijoy, NULL))) - { - os::Printer::log("Could not create DirectInput device", ELL_WARNING); - return; - } + JoystickInfo activeJoystick; + activeJoystick.Index=ActiveJoysticks.size(); + activeJoystick.guid=guid; + activeJoystick.Name=lpddi->tszProductName; + if (FAILED(DirectInputDevice->CreateDevice(guid, &activeJoystick.lpdijoy, NULL))) + { + os::Printer::log("Could not create DirectInput device", ELL_WARNING); + return; + } - activeJoystick.devcaps.dwSize=sizeof(activeJoystick.devcaps); - if (FAILED(activeJoystick.lpdijoy->GetCapabilities(&activeJoystick.devcaps))) - { - os::Printer::log("Could not create DirectInput device", ELL_WARNING); - return; - } + activeJoystick.devcaps.dwSize=sizeof(activeJoystick.devcaps); + if (FAILED(activeJoystick.lpdijoy->GetCapabilities(&activeJoystick.devcaps))) + { + os::Printer::log("Could not create DirectInput device", ELL_WARNING); + return; + } - if (FAILED(activeJoystick.lpdijoy->SetCooperativeLevel(Device->HWnd, DISCL_BACKGROUND | DISCL_EXCLUSIVE))) - { - os::Printer::log("Could not set DirectInput device cooperative level", ELL_WARNING); - return; - } + if (FAILED(activeJoystick.lpdijoy->SetCooperativeLevel(Device->HWnd, DISCL_BACKGROUND | DISCL_EXCLUSIVE))) + { + os::Printer::log("Could not set DirectInput device cooperative level", ELL_WARNING); + return; + } - if (FAILED(activeJoystick.lpdijoy->SetDataFormat(&c_dfDIJoystick2))) - { - os::Printer::log("Could not set DirectInput device data format", ELL_WARNING); - return; - } + if (FAILED(activeJoystick.lpdijoy->SetDataFormat(&c_dfDIJoystick2))) + { + os::Printer::log("Could not set DirectInput device data format", ELL_WARNING); + return; + } - if (FAILED(activeJoystick.lpdijoy->Acquire())) - { - os::Printer::log("Could not set DirectInput cooperative level", ELL_WARNING); - return; - } + if (FAILED(activeJoystick.lpdijoy->Acquire())) + { + os::Printer::log("Could not set DirectInput cooperative level", ELL_WARNING); + return; + } - DIJOYSTATE2 info; - if (FAILED(activeJoystick.lpdijoy->GetDeviceState(sizeof(info),&info))) - { - os::Printer::log("Could not read DirectInput device state", ELL_WARNING); - return; - } + DIJOYSTATE2 info; + if (FAILED(activeJoystick.lpdijoy->GetDeviceState(sizeof(info),&info))) + { + os::Printer::log("Could not read DirectInput device state", ELL_WARNING); + return; + } - ZeroMemory(activeJoystick.axisValid,sizeof(activeJoystick.axisValid)); - activeJoystick.axisValid[0]= (info.lX!=0) ? 1 : 0; - activeJoystick.axisValid[1]= (info.lY!=0) ? 1 : 0; - activeJoystick.axisValid[2]= (info.lZ!=0) ? 1 : 0; - activeJoystick.axisValid[3]= (info.lRx!=0) ? 1 : 0; - activeJoystick.axisValid[4]= (info.lRy!=0) ? 1 : 0; - activeJoystick.axisValid[5]= (info.lRz!=0) ? 1 : 0; + ZeroMemory(activeJoystick.axisValid,sizeof(activeJoystick.axisValid)); + activeJoystick.axisValid[0]= (info.lX!=0) ? 1 : 0; + activeJoystick.axisValid[1]= (info.lY!=0) ? 1 : 0; + activeJoystick.axisValid[2]= (info.lZ!=0) ? 1 : 0; + activeJoystick.axisValid[3]= (info.lRx!=0) ? 1 : 0; + activeJoystick.axisValid[4]= (info.lRy!=0) ? 1 : 0; + activeJoystick.axisValid[5]= (info.lRz!=0) ? 1 : 0; - int caxis=0; - for (u8 i=0; i<6; i++) - { - if (activeJoystick.axisValid[i]) - caxis++; - } + int caxis=0; + for (u8 i=0; i<6; i++) + { + if (activeJoystick.axisValid[i]) + caxis++; + } - for (u8 i=0; i<(activeJoystick.devcaps.dwAxes)-caxis; i++) - { - if (i+caxis < 8) - activeJoystick.axisValid[i+caxis]=1; - } + for (u8 i=0; i<(activeJoystick.devcaps.dwAxes)-caxis; i++) + { + if (i+caxis < 8) + activeJoystick.axisValid[i+caxis]=1; + } - ActiveJoysticks.push_back(activeJoystick); - } + ActiveJoysticks.push_back(activeJoystick); + } #endif void pollJoysticks() { #if defined _IRR_COMPILE_WITH_JOYSTICK_EVENTS_ #ifdef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ - if(0 == ActiveJoysticks.size()) - return; + if(0 == ActiveJoysticks.size()) + return; - u32 joystick; - DIJOYSTATE2 info; + u32 joystick; + DIJOYSTATE2 info; - for(joystick = 0; joystick < ActiveJoysticks.size(); ++joystick) - { - // needs to be reset for each joystick - // request ALL values and POV as continuous if possible + for(joystick = 0; joystick < ActiveJoysticks.size(); ++joystick) + { + // needs to be reset for each joystick + // request ALL values and POV as continuous if possible - const DIDEVCAPS & caps = ActiveJoysticks[joystick].devcaps; - // if no POV is available don't ask for POV values + const DIDEVCAPS & caps = ActiveJoysticks[joystick].devcaps; + // if no POV is available don't ask for POV values - if (!FAILED(ActiveJoysticks[joystick].lpdijoy->GetDeviceState(sizeof(info),&info))) - { - SEvent event; + if (!FAILED(ActiveJoysticks[joystick].lpdijoy->GetDeviceState(sizeof(info),&info))) + { + SEvent event; - event.EventType = irr::EET_JOYSTICK_INPUT_EVENT; - event.JoystickEvent.Joystick = (u8)joystick; + event.EventType = irr::EET_JOYSTICK_INPUT_EVENT; + event.JoystickEvent.Joystick = (u8)joystick; - event.JoystickEvent.POV = (u16)info.rgdwPOV[0]; - // set to undefined if no POV value was returned or the value - // is out of range - if ((caps.dwPOVs==0) || (event.JoystickEvent.POV > 35900)) - event.JoystickEvent.POV = 65535; + event.JoystickEvent.POV = (u16)info.rgdwPOV[0]; + // set to undefined if no POV value was returned or the value + // is out of range + if ((caps.dwPOVs==0) || (event.JoystickEvent.POV > 35900)) + event.JoystickEvent.POV = 65535; - for(int axis = 0; axis < SEvent::SJoystickEvent::NUMBER_OF_AXES; ++axis) - event.JoystickEvent.Axis[axis] = 0; + for(int axis = 0; axis < SEvent::SJoystickEvent::NUMBER_OF_AXES; ++axis) + event.JoystickEvent.Axis[axis] = 0; - u16 dxAxis=0; - u16 irrAxis=0; + u16 dxAxis=0; + u16 irrAxis=0; - while (dxAxis < 6 && irrAxis 0) - axisFound=1; + if (ActiveJoysticks[joystick].axisValid[dxAxis]>0) + axisFound=1; - if (axisFound) - { - s32 val=axisValue - 32768; + if (axisFound) + { + s32 val=axisValue - 32768; - if (val <-32767) val=-32767; - if (val > 32767) val=32767; - event.JoystickEvent.Axis[irrAxis]=(s16)(val); - irrAxis++; - } + if (val <-32767) val=-32767; + if (val > 32767) val=32767; + event.JoystickEvent.Axis[irrAxis]=(s16)(val); + irrAxis++; + } - dxAxis++; - } + dxAxis++; + } - u32 buttons=0; - BYTE* bytebuttons=info.rgbButtons; - for (u16 i=0; i<32; i++) - { - if (bytebuttons[i] >0) - { - buttons |= (1 << i); - } - } - event.JoystickEvent.ButtonStates = buttons; + u32 buttons=0; + BYTE* bytebuttons=info.rgbButtons; + for (u16 i=0; i<32; i++) + { + if (bytebuttons[i] >0) + { + buttons |= (1 << i); + } + } + event.JoystickEvent.ButtonStates = buttons; - (void)Device->postEventFromUser(event); - } - } + (void)Device->postEventFromUser(event); + } + } #else - if (0 == ActiveJoysticks.size()) - return; + if (0 == ActiveJoysticks.size()) + return; - u32 joystick; - JOYINFOEX info; + u32 joystick; + JOYINFOEX info; - for(joystick = 0; joystick < ActiveJoysticks.size(); ++joystick) - { - // needs to be reset for each joystick - // request ALL values and POV as continuous if possible - info.dwSize = sizeof(info); - info.dwFlags = JOY_RETURNALL|JOY_RETURNPOVCTS; - const JOYCAPS & caps = ActiveJoysticks[joystick].Caps; - // if no POV is available don't ask for POV values - if (!(caps.wCaps & JOYCAPS_HASPOV)) - info.dwFlags &= ~(JOY_RETURNPOV|JOY_RETURNPOVCTS); - if(JOYERR_NOERROR == joyGetPosEx(ActiveJoysticks[joystick].Index, &info)) - { - SEvent event; + for(joystick = 0; joystick < ActiveJoysticks.size(); ++joystick) + { + // needs to be reset for each joystick + // request ALL values and POV as continuous if possible + info.dwSize = sizeof(info); + info.dwFlags = JOY_RETURNALL|JOY_RETURNPOVCTS; + const JOYCAPS & caps = ActiveJoysticks[joystick].Caps; + // if no POV is available don't ask for POV values + if (!(caps.wCaps & JOYCAPS_HASPOV)) + info.dwFlags &= ~(JOY_RETURNPOV|JOY_RETURNPOVCTS); + if(JOYERR_NOERROR == joyGetPosEx(ActiveJoysticks[joystick].Index, &info)) + { + SEvent event; - event.EventType = irr::EET_JOYSTICK_INPUT_EVENT; - event.JoystickEvent.Joystick = (u8)joystick; + event.EventType = irr::EET_JOYSTICK_INPUT_EVENT; + event.JoystickEvent.Joystick = (u8)joystick; - event.JoystickEvent.POV = (u16)info.dwPOV; - // set to undefined if no POV value was returned or the value - // is out of range - if (!(info.dwFlags & JOY_RETURNPOV) || (event.JoystickEvent.POV > 35900)) - event.JoystickEvent.POV = 65535; + event.JoystickEvent.POV = (u16)info.dwPOV; + // set to undefined if no POV value was returned or the value + // is out of range + if (!(info.dwFlags & JOY_RETURNPOV) || (event.JoystickEvent.POV > 35900)) + event.JoystickEvent.POV = 65535; - for(int axis = 0; axis < SEvent::SJoystickEvent::NUMBER_OF_AXES; ++axis) - event.JoystickEvent.Axis[axis] = 0; + for(int axis = 0; axis < SEvent::SJoystickEvent::NUMBER_OF_AXES; ++axis) + event.JoystickEvent.Axis[axis] = 0; - event.JoystickEvent.ButtonStates = info.dwButtons; + event.JoystickEvent.ButtonStates = info.dwButtons; - switch(caps.wNumAxes) - { - default: - case 6: - event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_V] = - (s16)((65535 * (info.dwVpos - caps.wVmin)) / (caps.wVmax - caps.wVmin) - 32768); + switch(caps.wNumAxes) + { + default: + case 6: + event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_V] = + (s16)((65535 * (info.dwVpos - caps.wVmin)) / (caps.wVmax - caps.wVmin) - 32768); - case 5: - event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_U] = - (s16)((65535 * (info.dwUpos - caps.wUmin)) / (caps.wUmax - caps.wUmin) - 32768); + case 5: + event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_U] = + (s16)((65535 * (info.dwUpos - caps.wUmin)) / (caps.wUmax - caps.wUmin) - 32768); - case 4: - event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_R] = - (s16)((65535 * (info.dwRpos - caps.wRmin)) / (caps.wRmax - caps.wRmin) - 32768); + case 4: + event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_R] = + (s16)((65535 * (info.dwRpos - caps.wRmin)) / (caps.wRmax - caps.wRmin) - 32768); - case 3: - event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Z] = - (s16)((65535 * (info.dwZpos - caps.wZmin)) / (caps.wZmax - caps.wZmin) - 32768); + case 3: + event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Z] = + (s16)((65535 * (info.dwZpos - caps.wZmin)) / (caps.wZmax - caps.wZmin) - 32768); - case 2: - event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Y] = - (s16)((65535 * (info.dwYpos - caps.wYmin)) / (caps.wYmax - caps.wYmin) - 32768); + case 2: + event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Y] = + (s16)((65535 * (info.dwYpos - caps.wYmin)) / (caps.wYmax - caps.wYmin) - 32768); - case 1: - event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_X] = - (s16)((65535 * (info.dwXpos - caps.wXmin)) / (caps.wXmax - caps.wXmin) - 32768); - } + case 1: + event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_X] = + (s16)((65535 * (info.dwXpos - caps.wXmin)) / (caps.wXmax - caps.wXmin) - 32768); + } - (void)Device->postEventFromUser(event); - } - } + (void)Device->postEventFromUser(event); + } + } #endif #endif // _IRR_COMPILE_WITH_JOYSTICK_EVENTS_ } @@ -427,74 +427,74 @@ bool activateJoysticks(core::array & joystickInfo) { #if defined _IRR_COMPILE_WITH_JOYSTICK_EVENTS_ #ifdef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ - if (!DirectInputDevice || (DirectInputDevice->EnumDevices(DI8DEVCLASS_GAMECTRL, SJoystickWin32Control::EnumJoysticks, this, DIEDFL_ATTACHEDONLY ))) - { - os::Printer::log("Could not enum DirectInput8 controllers", ELL_WARNING); - return false; - } + if (!DirectInputDevice || (DirectInputDevice->EnumDevices(DI8DEVCLASS_GAMECTRL, SJoystickWin32Control::EnumJoysticks, this, DIEDFL_ATTACHEDONLY ))) + { + os::Printer::log("Could not enum DirectInput8 controllers", ELL_WARNING); + return false; + } - for(u32 joystick = 0; joystick < ActiveJoysticks.size(); ++joystick) - { - JoystickInfo& activeJoystick = ActiveJoysticks[joystick]; - SJoystickInfo info; - info.Axes=activeJoystick.devcaps.dwAxes; - info.Buttons=activeJoystick.devcaps.dwButtons; - info.Name=activeJoystick.Name; - info.PovHat = (activeJoystick.devcaps.dwPOVs != 0) - ? SJoystickInfo::POV_HAT_PRESENT : SJoystickInfo::POV_HAT_ABSENT; - joystickInfo.push_back(info); - } - return true; + for(u32 joystick = 0; joystick < ActiveJoysticks.size(); ++joystick) + { + JoystickInfo& activeJoystick = ActiveJoysticks[joystick]; + SJoystickInfo info; + info.Axes=activeJoystick.devcaps.dwAxes; + info.Buttons=activeJoystick.devcaps.dwButtons; + info.Name=activeJoystick.Name; + info.PovHat = (activeJoystick.devcaps.dwPOVs != 0) + ? SJoystickInfo::POV_HAT_PRESENT : SJoystickInfo::POV_HAT_ABSENT; + joystickInfo.push_back(info); + } + return true; #else - joystickInfo.clear(); - ActiveJoysticks.clear(); + joystickInfo.clear(); + ActiveJoysticks.clear(); - const u32 numberOfJoysticks = ::joyGetNumDevs(); - JOYINFOEX info; - info.dwSize = sizeof(info); - info.dwFlags = JOY_RETURNALL; + const u32 numberOfJoysticks = ::joyGetNumDevs(); + JOYINFOEX info; + info.dwSize = sizeof(info); + info.dwFlags = JOY_RETURNALL; - JoystickInfo activeJoystick; - SJoystickInfo returnInfo; + JoystickInfo activeJoystick; + SJoystickInfo returnInfo; - joystickInfo.reallocate(numberOfJoysticks); - ActiveJoysticks.reallocate(numberOfJoysticks); + joystickInfo.reallocate(numberOfJoysticks); + ActiveJoysticks.reallocate(numberOfJoysticks); - u32 joystick = 0; - for(; joystick < numberOfJoysticks; ++joystick) - { - if(JOYERR_NOERROR == joyGetPosEx(joystick, &info) - && - JOYERR_NOERROR == joyGetDevCaps(joystick, - &activeJoystick.Caps, - sizeof(activeJoystick.Caps))) - { - activeJoystick.Index = joystick; - ActiveJoysticks.push_back(activeJoystick); - returnInfo.Joystick = (u8)joystick; - returnInfo.Axes = activeJoystick.Caps.wNumAxes; - returnInfo.Buttons = activeJoystick.Caps.wNumButtons; - setJoystickName(joystick, activeJoystick.Caps, &returnInfo); - returnInfo.PovHat = ((activeJoystick.Caps.wCaps & JOYCAPS_HASPOV) == JOYCAPS_HASPOV) - ? SJoystickInfo::POV_HAT_PRESENT : SJoystickInfo::POV_HAT_ABSENT; + u32 joystick = 0; + for(; joystick < numberOfJoysticks; ++joystick) + { + if(JOYERR_NOERROR == joyGetPosEx(joystick, &info) + && + JOYERR_NOERROR == joyGetDevCaps(joystick, + &activeJoystick.Caps, + sizeof(activeJoystick.Caps))) + { + activeJoystick.Index = joystick; + ActiveJoysticks.push_back(activeJoystick); + returnInfo.Joystick = (u8)joystick; + returnInfo.Axes = activeJoystick.Caps.wNumAxes; + returnInfo.Buttons = activeJoystick.Caps.wNumButtons; + setJoystickName(joystick, activeJoystick.Caps, &returnInfo); + returnInfo.PovHat = ((activeJoystick.Caps.wCaps & JOYCAPS_HASPOV) == JOYCAPS_HASPOV) + ? SJoystickInfo::POV_HAT_PRESENT : SJoystickInfo::POV_HAT_ABSENT; - joystickInfo.push_back(returnInfo); - } - } + joystickInfo.push_back(returnInfo); + } + } - for(joystick = 0; joystick < joystickInfo.size(); ++joystick) - { - char logString[256]; - (void)sprintf(logString, "Found joystick %d, %d axes, %d buttons '%s'", - joystick, joystickInfo[joystick].Axes, - joystickInfo[joystick].Buttons, joystickInfo[joystick].Name.c_str()); - os::Printer::log(logString, ELL_INFORMATION); - } + for(joystick = 0; joystick < joystickInfo.size(); ++joystick) + { + char logString[256]; + (void)sprintf(logString, "Found joystick %d, %d axes, %d buttons '%s'", + joystick, joystickInfo[joystick].Axes, + joystickInfo[joystick].Buttons, joystickInfo[joystick].Name.c_str()); + os::Printer::log(logString, ELL_INFORMATION); + } - return true; + return true; #endif #else - return false; + return false; #endif // _IRR_COMPILE_WITH_JOYSTICK_EVENTS_ } }; @@ -504,536 +504,536 @@ bool activateJoysticks(core::array & joystickInfo) // Based on the table from http://www.science.co.il/Language/Locale-Codes.asp?s=decimal static unsigned int LocaleIdToCodepage(unsigned int lcid) { - switch ( lcid ) - { - case 1098: // Telugu - case 1095: // Gujarati - case 1094: // Punjabi - case 1103: // Sanskrit - case 1111: // Konkani - case 1114: // Syriac - case 1099: // Kannada - case 1102: // Marathi - case 1125: // Divehi - case 1067: // Armenian - case 1081: // Hindi - case 1079: // Georgian - case 1097: // Tamil - return 0; - case 1054: // Thai - return 874; - case 1041: // Japanese - return 932; - case 2052: // Chinese (PRC) - case 4100: // Chinese (Singapore) - return 936; - case 1042: // Korean - return 949; - case 5124: // Chinese (Macau S.A.R.) - case 3076: // Chinese (Hong Kong S.A.R.) - case 1028: // Chinese (Taiwan) - return 950; - case 1048: // Romanian - case 1060: // Slovenian - case 1038: // Hungarian - case 1051: // Slovak - case 1045: // Polish - case 1052: // Albanian - case 2074: // Serbian (Latin) - case 1050: // Croatian - case 1029: // Czech - return 1250; - case 1104: // Mongolian (Cyrillic) - case 1071: // FYRO Macedonian - case 2115: // Uzbek (Cyrillic) - case 1058: // Ukrainian - case 2092: // Azeri (Cyrillic) - case 1092: // Tatar - case 1087: // Kazakh - case 1059: // Belarusian - case 1088: // Kyrgyz (Cyrillic) - case 1026: // Bulgarian - case 3098: // Serbian (Cyrillic) - case 1049: // Russian - return 1251; - case 8201: // English (Jamaica) - case 3084: // French (Canada) - case 1036: // French (France) - case 5132: // French (Luxembourg) - case 5129: // English (New Zealand) - case 6153: // English (Ireland) - case 1043: // Dutch (Netherlands) - case 9225: // English (Caribbean) - case 4108: // French (Switzerland) - case 4105: // English (Canada) - case 1110: // Galician - case 10249: // English (Belize) - case 3079: // German (Austria) - case 6156: // French (Monaco) - case 12297: // English (Zimbabwe) - case 1069: // Basque - case 2067: // Dutch (Belgium) - case 2060: // French (Belgium) - case 1035: // Finnish - case 1080: // Faroese - case 1031: // German (Germany) - case 3081: // English (Australia) - case 1033: // English (United States) - case 2057: // English (United Kingdom) - case 1027: // Catalan - case 11273: // English (Trinidad) - case 7177: // English (South Africa) - case 1030: // Danish - case 13321: // English (Philippines) - case 15370: // Spanish (Paraguay) - case 9226: // Spanish (Colombia) - case 5130: // Spanish (Costa Rica) - case 7178: // Spanish (Dominican Republic) - case 12298: // Spanish (Ecuador) - case 17418: // Spanish (El Salvador) - case 4106: // Spanish (Guatemala) - case 18442: // Spanish (Honduras) - case 3082: // Spanish (International Sort) - case 13322: // Spanish (Chile) - case 19466: // Spanish (Nicaragua) - case 2058: // Spanish (Mexico) - case 10250: // Spanish (Peru) - case 20490: // Spanish (Puerto Rico) - case 1034: // Spanish (Traditional Sort) - case 14346: // Spanish (Uruguay) - case 8202: // Spanish (Venezuela) - case 1089: // Swahili - case 1053: // Swedish - case 2077: // Swedish (Finland) - case 5127: // German (Liechtenstein) - case 1078: // Afrikaans - case 6154: // Spanish (Panama) - case 4103: // German (Luxembourg) - case 16394: // Spanish (Bolivia) - case 2055: // German (Switzerland) - case 1039: // Icelandic - case 1057: // Indonesian - case 1040: // Italian (Italy) - case 2064: // Italian (Switzerland) - case 2068: // Norwegian (Nynorsk) - case 11274: // Spanish (Argentina) - case 1046: // Portuguese (Brazil) - case 1044: // Norwegian (Bokmal) - case 1086: // Malay (Malaysia) - case 2110: // Malay (Brunei Darussalam) - case 2070: // Portuguese (Portugal) - return 1252; - case 1032: // Greek - return 1253; - case 1091: // Uzbek (Latin) - case 1068: // Azeri (Latin) - case 1055: // Turkish - return 1254; - case 1037: // Hebrew - return 1255; - case 5121: // Arabic (Algeria) - case 15361: // Arabic (Bahrain) - case 9217: // Arabic (Yemen) - case 3073: // Arabic (Egypt) - case 2049: // Arabic (Iraq) - case 11265: // Arabic (Jordan) - case 13313: // Arabic (Kuwait) - case 12289: // Arabic (Lebanon) - case 4097: // Arabic (Libya) - case 6145: // Arabic (Morocco) - case 8193: // Arabic (Oman) - case 16385: // Arabic (Qatar) - case 1025: // Arabic (Saudi Arabia) - case 10241: // Arabic (Syria) - case 14337: // Arabic (U.A.E.) - case 1065: // Farsi - case 1056: // Urdu - case 7169: // Arabic (Tunisia) - return 1256; - case 1061: // Estonian - case 1062: // Latvian - case 1063: // Lithuanian - return 1257; - case 1066: // Vietnamese - return 1258; - } - return 65001; // utf-8 + switch ( lcid ) + { + case 1098: // Telugu + case 1095: // Gujarati + case 1094: // Punjabi + case 1103: // Sanskrit + case 1111: // Konkani + case 1114: // Syriac + case 1099: // Kannada + case 1102: // Marathi + case 1125: // Divehi + case 1067: // Armenian + case 1081: // Hindi + case 1079: // Georgian + case 1097: // Tamil + return 0; + case 1054: // Thai + return 874; + case 1041: // Japanese + return 932; + case 2052: // Chinese (PRC) + case 4100: // Chinese (Singapore) + return 936; + case 1042: // Korean + return 949; + case 5124: // Chinese (Macau S.A.R.) + case 3076: // Chinese (Hong Kong S.A.R.) + case 1028: // Chinese (Taiwan) + return 950; + case 1048: // Romanian + case 1060: // Slovenian + case 1038: // Hungarian + case 1051: // Slovak + case 1045: // Polish + case 1052: // Albanian + case 2074: // Serbian (Latin) + case 1050: // Croatian + case 1029: // Czech + return 1250; + case 1104: // Mongolian (Cyrillic) + case 1071: // FYRO Macedonian + case 2115: // Uzbek (Cyrillic) + case 1058: // Ukrainian + case 2092: // Azeri (Cyrillic) + case 1092: // Tatar + case 1087: // Kazakh + case 1059: // Belarusian + case 1088: // Kyrgyz (Cyrillic) + case 1026: // Bulgarian + case 3098: // Serbian (Cyrillic) + case 1049: // Russian + return 1251; + case 8201: // English (Jamaica) + case 3084: // French (Canada) + case 1036: // French (France) + case 5132: // French (Luxembourg) + case 5129: // English (New Zealand) + case 6153: // English (Ireland) + case 1043: // Dutch (Netherlands) + case 9225: // English (Caribbean) + case 4108: // French (Switzerland) + case 4105: // English (Canada) + case 1110: // Galician + case 10249: // English (Belize) + case 3079: // German (Austria) + case 6156: // French (Monaco) + case 12297: // English (Zimbabwe) + case 1069: // Basque + case 2067: // Dutch (Belgium) + case 2060: // French (Belgium) + case 1035: // Finnish + case 1080: // Faroese + case 1031: // German (Germany) + case 3081: // English (Australia) + case 1033: // English (United States) + case 2057: // English (United Kingdom) + case 1027: // Catalan + case 11273: // English (Trinidad) + case 7177: // English (South Africa) + case 1030: // Danish + case 13321: // English (Philippines) + case 15370: // Spanish (Paraguay) + case 9226: // Spanish (Colombia) + case 5130: // Spanish (Costa Rica) + case 7178: // Spanish (Dominican Republic) + case 12298: // Spanish (Ecuador) + case 17418: // Spanish (El Salvador) + case 4106: // Spanish (Guatemala) + case 18442: // Spanish (Honduras) + case 3082: // Spanish (International Sort) + case 13322: // Spanish (Chile) + case 19466: // Spanish (Nicaragua) + case 2058: // Spanish (Mexico) + case 10250: // Spanish (Peru) + case 20490: // Spanish (Puerto Rico) + case 1034: // Spanish (Traditional Sort) + case 14346: // Spanish (Uruguay) + case 8202: // Spanish (Venezuela) + case 1089: // Swahili + case 1053: // Swedish + case 2077: // Swedish (Finland) + case 5127: // German (Liechtenstein) + case 1078: // Afrikaans + case 6154: // Spanish (Panama) + case 4103: // German (Luxembourg) + case 16394: // Spanish (Bolivia) + case 2055: // German (Switzerland) + case 1039: // Icelandic + case 1057: // Indonesian + case 1040: // Italian (Italy) + case 2064: // Italian (Switzerland) + case 2068: // Norwegian (Nynorsk) + case 11274: // Spanish (Argentina) + case 1046: // Portuguese (Brazil) + case 1044: // Norwegian (Bokmal) + case 1086: // Malay (Malaysia) + case 2110: // Malay (Brunei Darussalam) + case 2070: // Portuguese (Portugal) + return 1252; + case 1032: // Greek + return 1253; + case 1091: // Uzbek (Latin) + case 1068: // Azeri (Latin) + case 1055: // Turkish + return 1254; + case 1037: // Hebrew + return 1255; + case 5121: // Arabic (Algeria) + case 15361: // Arabic (Bahrain) + case 9217: // Arabic (Yemen) + case 3073: // Arabic (Egypt) + case 2049: // Arabic (Iraq) + case 11265: // Arabic (Jordan) + case 13313: // Arabic (Kuwait) + case 12289: // Arabic (Lebanon) + case 4097: // Arabic (Libya) + case 6145: // Arabic (Morocco) + case 8193: // Arabic (Oman) + case 16385: // Arabic (Qatar) + case 1025: // Arabic (Saudi Arabia) + case 10241: // Arabic (Syria) + case 14337: // Arabic (U.A.E.) + case 1065: // Farsi + case 1056: // Urdu + case 7169: // Arabic (Tunisia) + return 1256; + case 1061: // Estonian + case 1062: // Latvian + case 1063: // Lithuanian + return 1257; + case 1066: // Vietnamese + return 1258; + } + return 65001; // utf-8 } namespace { - struct SEnvMapper - { - HWND hWnd; - irr::CIrrDeviceWin32* irrDev; - }; - irr::core::list EnvMap; + struct SEnvMapper + { + HWND hWnd; + irr::CIrrDeviceWin32* irrDev; + }; + irr::core::list EnvMap; - HKL KEYBOARD_INPUT_HKL=0; - unsigned int KEYBOARD_INPUT_CODEPAGE = 1252; + HKL KEYBOARD_INPUT_HKL=0; + unsigned int KEYBOARD_INPUT_CODEPAGE = 1252; } SEnvMapper* getEnvMapperFromHWnd(HWND hWnd) { - irr::core::list::Iterator it = EnvMap.begin(); - for (; it!= EnvMap.end(); ++it) - if ((*it).hWnd == hWnd) - return &(*it); + irr::core::list::Iterator it = EnvMap.begin(); + for (; it!= EnvMap.end(); ++it) + if ((*it).hWnd == hWnd) + return &(*it); - return 0; + return 0; } irr::CIrrDeviceWin32* getDeviceFromHWnd(HWND hWnd) { - irr::core::list::Iterator it = EnvMap.begin(); - for (; it!= EnvMap.end(); ++it) - if ((*it).hWnd == hWnd) - return (*it).irrDev; + irr::core::list::Iterator it = EnvMap.begin(); + for (; it!= EnvMap.end(); ++it) + if ((*it).hWnd == hWnd) + return (*it).irrDev; - return 0; + return 0; } namespace irr { - void updateICPos(void* hWnd, s32 x, s32 y, s32 height) - { - COMPOSITIONFORM cf; - HWND hwnd = (HWND)hWnd; - HIMC hIMC = ImmGetContext(hwnd); + void updateICPos(void* hWnd, s32 x, s32 y, s32 height) + { + COMPOSITIONFORM cf; + HWND hwnd = (HWND)hWnd; + HIMC hIMC = ImmGetContext(hwnd); - if(hIMC) - { - cf.dwStyle = CFS_POINT; - cf.ptCurrentPos.x = x; - cf.ptCurrentPos.y = y - height; + if(hIMC) + { + cf.dwStyle = CFS_POINT; + cf.ptCurrentPos.x = x; + cf.ptCurrentPos.y = y - height; - ImmSetCompositionWindow(hIMC, &cf); + ImmSetCompositionWindow(hIMC, &cf); - ImmReleaseContext(hwnd, hIMC); - } - } + ImmReleaseContext(hwnd, hIMC); + } + } } // end of namespace irr LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { - #ifndef WM_MOUSEWHEEL - #define WM_MOUSEWHEEL 0x020A - #endif - #ifndef WHEEL_DELTA - #define WHEEL_DELTA 120 - #endif + #ifndef WM_MOUSEWHEEL + #define WM_MOUSEWHEEL 0x020A + #endif + #ifndef WHEEL_DELTA + #define WHEEL_DELTA 120 + #endif - irr::CIrrDeviceWin32* dev = 0; - irr::SEvent event; + irr::CIrrDeviceWin32* dev = 0; + irr::SEvent event; - static irr::s32 ClickCount=0; - if (GetCapture() != hWnd && ClickCount > 0) - ClickCount = 0; + static irr::s32 ClickCount=0; + if (GetCapture() != hWnd && ClickCount > 0) + ClickCount = 0; - struct messageMap - { - irr::s32 group; - UINT winMessage; - irr::s32 irrMessage; - }; + struct messageMap + { + irr::s32 group; + UINT winMessage; + irr::s32 irrMessage; + }; - static messageMap mouseMap[] = - { - {0, WM_LBUTTONDOWN, irr::EMIE_LMOUSE_PRESSED_DOWN}, - {1, WM_LBUTTONUP, irr::EMIE_LMOUSE_LEFT_UP}, - {0, WM_RBUTTONDOWN, irr::EMIE_RMOUSE_PRESSED_DOWN}, - {1, WM_RBUTTONUP, irr::EMIE_RMOUSE_LEFT_UP}, - {0, WM_MBUTTONDOWN, irr::EMIE_MMOUSE_PRESSED_DOWN}, - {1, WM_MBUTTONUP, irr::EMIE_MMOUSE_LEFT_UP}, - {2, WM_MOUSEMOVE, irr::EMIE_MOUSE_MOVED}, - {3, WM_MOUSEWHEEL, irr::EMIE_MOUSE_WHEEL}, - {-1, 0, 0} - }; + static messageMap mouseMap[] = + { + {0, WM_LBUTTONDOWN, irr::EMIE_LMOUSE_PRESSED_DOWN}, + {1, WM_LBUTTONUP, irr::EMIE_LMOUSE_LEFT_UP}, + {0, WM_RBUTTONDOWN, irr::EMIE_RMOUSE_PRESSED_DOWN}, + {1, WM_RBUTTONUP, irr::EMIE_RMOUSE_LEFT_UP}, + {0, WM_MBUTTONDOWN, irr::EMIE_MMOUSE_PRESSED_DOWN}, + {1, WM_MBUTTONUP, irr::EMIE_MMOUSE_LEFT_UP}, + {2, WM_MOUSEMOVE, irr::EMIE_MOUSE_MOVED}, + {3, WM_MOUSEWHEEL, irr::EMIE_MOUSE_WHEEL}, + {-1, 0, 0} + }; - // handle grouped events - messageMap * m = mouseMap; - while ( m->group >=0 && m->winMessage != message ) - m += 1; + // handle grouped events + messageMap * m = mouseMap; + while ( m->group >=0 && m->winMessage != message ) + m += 1; - if ( m->group >= 0 ) - { - if ( m->group == 0 ) // down - { - ClickCount++; - SetCapture(hWnd); - } - else - if ( m->group == 1 ) // up - { - ClickCount--; - if (ClickCount<1) - { - ClickCount=0; - ReleaseCapture(); - } - } + if ( m->group >= 0 ) + { + if ( m->group == 0 ) // down + { + ClickCount++; + SetCapture(hWnd); + } + else + if ( m->group == 1 ) // up + { + ClickCount--; + if (ClickCount<1) + { + ClickCount=0; + ReleaseCapture(); + } + } - event.EventType = irr::EET_MOUSE_INPUT_EVENT; - event.MouseInput.Event = (irr::EMOUSE_INPUT_EVENT) m->irrMessage; - event.MouseInput.X = (short)LOWORD(lParam); - event.MouseInput.Y = (short)HIWORD(lParam); - event.MouseInput.Shift = ((LOWORD(wParam) & MK_SHIFT) != 0); - event.MouseInput.Control = ((LOWORD(wParam) & MK_CONTROL) != 0); - // left and right mouse buttons - event.MouseInput.ButtonStates = wParam & ( MK_LBUTTON | MK_RBUTTON); - // middle and extra buttons - if (wParam & MK_MBUTTON) - event.MouseInput.ButtonStates |= irr::EMBSM_MIDDLE; + event.EventType = irr::EET_MOUSE_INPUT_EVENT; + event.MouseInput.Event = (irr::EMOUSE_INPUT_EVENT) m->irrMessage; + event.MouseInput.X = (short)LOWORD(lParam); + event.MouseInput.Y = (short)HIWORD(lParam); + event.MouseInput.Shift = ((LOWORD(wParam) & MK_SHIFT) != 0); + event.MouseInput.Control = ((LOWORD(wParam) & MK_CONTROL) != 0); + // left and right mouse buttons + event.MouseInput.ButtonStates = wParam & ( MK_LBUTTON | MK_RBUTTON); + // middle and extra buttons + if (wParam & MK_MBUTTON) + event.MouseInput.ButtonStates |= irr::EMBSM_MIDDLE; #if(_WIN32_WINNT >= 0x0500) - if (wParam & MK_XBUTTON1) - event.MouseInput.ButtonStates |= irr::EMBSM_EXTRA1; - if (wParam & MK_XBUTTON2) - event.MouseInput.ButtonStates |= irr::EMBSM_EXTRA2; + if (wParam & MK_XBUTTON1) + event.MouseInput.ButtonStates |= irr::EMBSM_EXTRA1; + if (wParam & MK_XBUTTON2) + event.MouseInput.ButtonStates |= irr::EMBSM_EXTRA2; #endif - event.MouseInput.Wheel = 0.f; + event.MouseInput.Wheel = 0.f; - // wheel - if ( m->group == 3 ) - { - POINT p; // fixed by jox - p.x = 0; p.y = 0; - ClientToScreen(hWnd, &p); - event.MouseInput.X -= p.x; - event.MouseInput.Y -= p.y; - event.MouseInput.Wheel = ((irr::f32)((short)HIWORD(wParam))) / (irr::f32)WHEEL_DELTA; - } + // wheel + if ( m->group == 3 ) + { + POINT p; // fixed by jox + p.x = 0; p.y = 0; + ClientToScreen(hWnd, &p); + event.MouseInput.X -= p.x; + event.MouseInput.Y -= p.y; + event.MouseInput.Wheel = ((irr::f32)((short)HIWORD(wParam))) / (irr::f32)WHEEL_DELTA; + } - dev = getDeviceFromHWnd(hWnd); - if (dev) - { - dev->postEventFromUser(event); + dev = getDeviceFromHWnd(hWnd); + if (dev) + { + dev->postEventFromUser(event); - if ( event.MouseInput.Event >= irr::EMIE_LMOUSE_PRESSED_DOWN && event.MouseInput.Event <= irr::EMIE_MMOUSE_PRESSED_DOWN ) - { - irr::u32 clicks = dev->checkSuccessiveClicks(event.MouseInput.X, event.MouseInput.Y, event.MouseInput.Event); - if ( clicks == 2 ) - { - event.MouseInput.Event = (irr::EMOUSE_INPUT_EVENT)(irr::EMIE_LMOUSE_DOUBLE_CLICK + event.MouseInput.Event-irr::EMIE_LMOUSE_PRESSED_DOWN); - dev->postEventFromUser(event); - } - else if ( clicks == 3 ) - { - event.MouseInput.Event = (irr::EMOUSE_INPUT_EVENT)(irr::EMIE_LMOUSE_TRIPLE_CLICK + event.MouseInput.Event-irr::EMIE_LMOUSE_PRESSED_DOWN); - dev->postEventFromUser(event); - } - } - } + if ( event.MouseInput.Event >= irr::EMIE_LMOUSE_PRESSED_DOWN && event.MouseInput.Event <= irr::EMIE_MMOUSE_PRESSED_DOWN ) + { + irr::u32 clicks = dev->checkSuccessiveClicks(event.MouseInput.X, event.MouseInput.Y, event.MouseInput.Event); + if ( clicks == 2 ) + { + event.MouseInput.Event = (irr::EMOUSE_INPUT_EVENT)(irr::EMIE_LMOUSE_DOUBLE_CLICK + event.MouseInput.Event-irr::EMIE_LMOUSE_PRESSED_DOWN); + dev->postEventFromUser(event); + } + else if ( clicks == 3 ) + { + event.MouseInput.Event = (irr::EMOUSE_INPUT_EVENT)(irr::EMIE_LMOUSE_TRIPLE_CLICK + event.MouseInput.Event-irr::EMIE_LMOUSE_PRESSED_DOWN); + dev->postEventFromUser(event); + } + } + } - if (m->irrMessage == irr::EMIE_LMOUSE_PRESSED_DOWN || m->irrMessage == irr::EMIE_LMOUSE_LEFT_UP) - { - event.EventType = irr::EET_IMPUT_METHOD_EVENT; - event.InputMethodEvent.Event = irr::EIME_CHANGE_POS; - event.InputMethodEvent.Handle = hWnd; + if (m->irrMessage == irr::EMIE_LMOUSE_PRESSED_DOWN || m->irrMessage == irr::EMIE_LMOUSE_LEFT_UP) + { + event.EventType = irr::EET_IMPUT_METHOD_EVENT; + event.InputMethodEvent.Event = irr::EIME_CHANGE_POS; + event.InputMethodEvent.Handle = hWnd; - if (dev) - dev->postEventFromUser(event); - } - return 0; - } + if (dev) + dev->postEventFromUser(event); + } + return 0; + } - switch (message) - { - case WM_PAINT: - { - PAINTSTRUCT ps; - BeginPaint(hWnd, &ps); - EndPaint(hWnd, &ps); - } - return 0; + switch (message) + { + case WM_PAINT: + { + PAINTSTRUCT ps; + BeginPaint(hWnd, &ps); + EndPaint(hWnd, &ps); + } + return 0; - case WM_ERASEBKGND: - return 0; + case WM_ERASEBKGND: + return 0; - case WM_IME_CHAR: - { - event.EventType = irr::EET_IMPUT_METHOD_EVENT; - event.InputMethodEvent.Event = irr::EIME_CHAR_INPUT; - event.InputMethodEvent.Handle = hWnd; + case WM_IME_CHAR: + { + event.EventType = irr::EET_IMPUT_METHOD_EVENT; + event.InputMethodEvent.Event = irr::EIME_CHAR_INPUT; + event.InputMethodEvent.Handle = hWnd; - event.KeyInput.Char = 0; - event.KeyInput.Key = irr::IRR_KEY_OEM_CLEAR; - event.KeyInput.Shift = 0; - event.KeyInput.Control = 0; + event.KeyInput.Char = 0; + event.KeyInput.Key = irr::IRR_KEY_OEM_CLEAR; + event.KeyInput.Shift = 0; + event.KeyInput.Control = 0; - char bits[2] = { (char)((wParam & 0xff00) >> 8), (char)(wParam & 0xff) }; - if (bits[0] == 0) - event.InputMethodEvent.Char = (wchar_t)wParam; - else - MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, bits, 2, &event.InputMethodEvent.Char, 1); + char bits[2] = { (char)((wParam & 0xff00) >> 8), (char)(wParam & 0xff) }; + if (bits[0] == 0) + event.InputMethodEvent.Char = (wchar_t)wParam; + else + MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, bits, 2, &event.InputMethodEvent.Char, 1); - dev = getDeviceFromHWnd(hWnd); - if (dev) - dev->postEventFromUser(event); + dev = getDeviceFromHWnd(hWnd); + if (dev) + dev->postEventFromUser(event); - return 0; - } + return 0; + } - case WM_SYSKEYDOWN: - case WM_SYSKEYUP: - case WM_KEYDOWN: - case WM_KEYUP: - { - BYTE allKeys[256]; + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_KEYDOWN: + case WM_KEYUP: + { + BYTE allKeys[256]; - event.EventType = irr::EET_KEY_INPUT_EVENT; - event.KeyInput.Key = (irr::EKEY_CODE)wParam; - event.KeyInput.PressedDown = (message==WM_KEYDOWN || message == WM_SYSKEYDOWN); + event.EventType = irr::EET_KEY_INPUT_EVENT; + event.KeyInput.Key = (irr::EKEY_CODE)wParam; + event.KeyInput.PressedDown = (message==WM_KEYDOWN || message == WM_SYSKEYDOWN); - const UINT MY_MAPVK_VSC_TO_VK_EX = 3; // MAPVK_VSC_TO_VK_EX should be in SDK according to MSDN, but isn't in mine. - if ( event.KeyInput.Key == irr::IRR_KEY_SHIFT ) - { - // this will fail on systems before windows NT/2000/XP, not sure _what_ will return there instead. - event.KeyInput.Key = (irr::EKEY_CODE)MapVirtualKey( ((lParam>>16) & 255), MY_MAPVK_VSC_TO_VK_EX ); - } - if ( event.KeyInput.Key == irr::IRR_KEY_CONTROL ) - { - event.KeyInput.Key = (irr::EKEY_CODE)MapVirtualKey( ((lParam>>16) & 255), MY_MAPVK_VSC_TO_VK_EX ); - // some keyboards will just return LEFT for both - left and right keys. So also check extend bit. - if (lParam & 0x1000000) - event.KeyInput.Key = irr::IRR_KEY_RCONTROL; - } - if ( event.KeyInput.Key == irr::IRR_KEY_MENU ) - { - event.KeyInput.Key = (irr::EKEY_CODE)MapVirtualKey( ((lParam>>16) & 255), MY_MAPVK_VSC_TO_VK_EX ); - if (lParam & 0x1000000) - event.KeyInput.Key = irr::IRR_KEY_RMENU; - } + const UINT MY_MAPVK_VSC_TO_VK_EX = 3; // MAPVK_VSC_TO_VK_EX should be in SDK according to MSDN, but isn't in mine. + if ( event.KeyInput.Key == irr::IRR_KEY_SHIFT ) + { + // this will fail on systems before windows NT/2000/XP, not sure _what_ will return there instead. + event.KeyInput.Key = (irr::EKEY_CODE)MapVirtualKey( ((lParam>>16) & 255), MY_MAPVK_VSC_TO_VK_EX ); + } + if ( event.KeyInput.Key == irr::IRR_KEY_CONTROL ) + { + event.KeyInput.Key = (irr::EKEY_CODE)MapVirtualKey( ((lParam>>16) & 255), MY_MAPVK_VSC_TO_VK_EX ); + // some keyboards will just return LEFT for both - left and right keys. So also check extend bit. + if (lParam & 0x1000000) + event.KeyInput.Key = irr::IRR_KEY_RCONTROL; + } + if ( event.KeyInput.Key == irr::IRR_KEY_MENU ) + { + event.KeyInput.Key = (irr::EKEY_CODE)MapVirtualKey( ((lParam>>16) & 255), MY_MAPVK_VSC_TO_VK_EX ); + if (lParam & 0x1000000) + event.KeyInput.Key = irr::IRR_KEY_RMENU; + } - GetKeyboardState(allKeys); + GetKeyboardState(allKeys); - event.KeyInput.Shift = ((allKeys[VK_SHIFT] & 0x80)!=0); - event.KeyInput.Control = ((allKeys[VK_CONTROL] & 0x80)!=0); + event.KeyInput.Shift = ((allKeys[VK_SHIFT] & 0x80)!=0); + event.KeyInput.Control = ((allKeys[VK_CONTROL] & 0x80)!=0); - // Handle unicode and deadkeys in a way that works since Windows 95 and nt4.0 - // Using ToUnicode instead would be shorter, but would to my knowledge not run on 95 and 98. - WORD keyChars[2]; - UINT scanCode = HIWORD(lParam); - int conversionResult = ToAsciiEx(wParam,scanCode,allKeys,keyChars,0,KEYBOARD_INPUT_HKL); - if (conversionResult == 1) - { - WORD unicodeChar; - MultiByteToWideChar( - KEYBOARD_INPUT_CODEPAGE, - MB_PRECOMPOSED, // default - (LPCSTR)keyChars, - sizeof(keyChars), - (WCHAR*)&unicodeChar, - 1 ); - event.KeyInput.Char = unicodeChar; - } - else - event.KeyInput.Char = 0; + // Handle unicode and deadkeys in a way that works since Windows 95 and nt4.0 + // Using ToUnicode instead would be shorter, but would to my knowledge not run on 95 and 98. + WORD keyChars[2]; + UINT scanCode = HIWORD(lParam); + int conversionResult = ToAsciiEx(wParam,scanCode,allKeys,keyChars,0,KEYBOARD_INPUT_HKL); + if (conversionResult == 1) + { + WORD unicodeChar; + MultiByteToWideChar( + KEYBOARD_INPUT_CODEPAGE, + MB_PRECOMPOSED, // default + (LPCSTR)keyChars, + sizeof(keyChars), + (WCHAR*)&unicodeChar, + 1 ); + event.KeyInput.Char = unicodeChar; + } + else + event.KeyInput.Char = 0; - // allow composing characters like '@' with Alt Gr on non-US keyboards - if ((allKeys[VK_MENU] & 0x80) != 0) - event.KeyInput.Control = 0; + // allow composing characters like '@' with Alt Gr on non-US keyboards + if ((allKeys[VK_MENU] & 0x80) != 0) + event.KeyInput.Control = 0; - dev = getDeviceFromHWnd(hWnd); - if (dev) - dev->postEventFromUser(event); + dev = getDeviceFromHWnd(hWnd); + if (dev) + dev->postEventFromUser(event); - event.EventType = irr::EET_IMPUT_METHOD_EVENT; - event.InputMethodEvent.Event = irr::EIME_CHANGE_POS; - event.InputMethodEvent.Handle = hWnd; + event.EventType = irr::EET_IMPUT_METHOD_EVENT; + event.InputMethodEvent.Event = irr::EIME_CHANGE_POS; + event.InputMethodEvent.Handle = hWnd; - if (dev) - dev->postEventFromUser(event); + if (dev) + dev->postEventFromUser(event); - if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP) - return DefWindowProc(hWnd, message, wParam, lParam); - else - return 0; - } + if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP) + return DefWindowProc(hWnd, message, wParam, lParam); + else + return 0; + } - case WM_SIZE: - { - // resize - dev = getDeviceFromHWnd(hWnd); - if (dev) - dev->OnResized(); - } - return 0; + case WM_SIZE: + { + // resize + dev = getDeviceFromHWnd(hWnd); + if (dev) + dev->OnResized(); + } + return 0; - case WM_DESTROY: - PostQuitMessage(0); - return 0; + case WM_DESTROY: + PostQuitMessage(0); + return 0; - case WM_SYSCOMMAND: - // prevent screensaver or monitor powersave mode from starting - if ((wParam & 0xFFF0) == SC_SCREENSAVE || - (wParam & 0xFFF0) == SC_MONITORPOWER || - (wParam & 0xFFF0) == SC_KEYMENU - ) - return 0; + case WM_SYSCOMMAND: + // prevent screensaver or monitor powersave mode from starting + if ((wParam & 0xFFF0) == SC_SCREENSAVE || + (wParam & 0xFFF0) == SC_MONITORPOWER || + (wParam & 0xFFF0) == SC_KEYMENU + ) + return 0; - break; + break; - case WM_ACTIVATE: - // we need to take care for screen changes, e.g. Alt-Tab - dev = getDeviceFromHWnd(hWnd); - if (dev && dev->isFullscreen()) - { - if ((wParam&0xFF)==WA_INACTIVE) - { - // If losing focus we minimize the app to show other one - ShowWindow(hWnd,SW_MINIMIZE); - // and switch back to default resolution - dev->switchToFullScreen(true); - } - else - { - // Otherwise we retore the fullscreen Irrlicht app - SetForegroundWindow(hWnd); - ShowWindow(hWnd, SW_RESTORE); - // and set the fullscreen resolution again - dev->switchToFullScreen(); - } - } - event.EventType = irr::EET_IMPUT_METHOD_EVENT; - event.InputMethodEvent.Event = irr::EIME_CHANGE_POS; - event.InputMethodEvent.Handle = hWnd; + case WM_ACTIVATE: + // we need to take care for screen changes, e.g. Alt-Tab + dev = getDeviceFromHWnd(hWnd); + if (dev && dev->isFullscreen()) + { + if ((wParam&0xFF)==WA_INACTIVE) + { + // If losing focus we minimize the app to show other one + ShowWindow(hWnd,SW_MINIMIZE); + // and switch back to default resolution + dev->switchToFullScreen(true); + } + else + { + // Otherwise we retore the fullscreen Irrlicht app + SetForegroundWindow(hWnd); + ShowWindow(hWnd, SW_RESTORE); + // and set the fullscreen resolution again + dev->switchToFullScreen(); + } + } + event.EventType = irr::EET_IMPUT_METHOD_EVENT; + event.InputMethodEvent.Event = irr::EIME_CHANGE_POS; + event.InputMethodEvent.Handle = hWnd; - if (dev) - dev->postEventFromUser(event); - break; + if (dev) + dev->postEventFromUser(event); + break; - case WM_USER: - event.EventType = irr::EET_USER_EVENT; - event.UserEvent.UserData1 = (irr::s32)wParam; - event.UserEvent.UserData2 = (irr::s32)lParam; - dev = getDeviceFromHWnd(hWnd); + case WM_USER: + event.EventType = irr::EET_USER_EVENT; + event.UserEvent.UserData1 = (irr::s32)wParam; + event.UserEvent.UserData2 = (irr::s32)lParam; + dev = getDeviceFromHWnd(hWnd); - if (dev) - dev->postEventFromUser(event); + if (dev) + dev->postEventFromUser(event); - return 0; + return 0; - case WM_SETCURSOR: - // because Windows forgot about that in the meantime - dev = getDeviceFromHWnd(hWnd); - if (dev) - { - dev->getCursorControl()->setActiveIcon( dev->getCursorControl()->getActiveIcon() ); - dev->getCursorControl()->setVisible( dev->getCursorControl()->isVisible() ); - } - break; + case WM_SETCURSOR: + // because Windows forgot about that in the meantime + dev = getDeviceFromHWnd(hWnd); + if (dev) + { + dev->getCursorControl()->setActiveIcon( dev->getCursorControl()->getActiveIcon() ); + dev->getCursorControl()->setVisible( dev->getCursorControl()->isVisible() ); + } + break; - case WM_INPUTLANGCHANGE: - // get the new codepage used for keyboard input - KEYBOARD_INPUT_HKL = GetKeyboardLayout(0); - KEYBOARD_INPUT_CODEPAGE = LocaleIdToCodepage( LOWORD(KEYBOARD_INPUT_HKL) ); - return 0; - } - return DefWindowProc(hWnd, message, wParam, lParam); + case WM_INPUTLANGCHANGE: + // get the new codepage used for keyboard input + KEYBOARD_INPUT_HKL = GetKeyboardLayout(0); + KEYBOARD_INPUT_CODEPAGE = LocaleIdToCodepage( LOWORD(KEYBOARD_INPUT_HKL) ); + return 0; + } + return DefWindowProc(hWnd, message, wParam, lParam); } @@ -1043,509 +1043,509 @@ namespace irr //! constructor CIrrDeviceWin32::CIrrDeviceWin32(const SIrrlichtCreationParameters& params) : CIrrDeviceStub(params), HWnd(0), ChangedToFullScreen(false), Resized(false), - ExternalWindow(false), Win32CursorControl(0), JoyControl(0) + ExternalWindow(false), Win32CursorControl(0), JoyControl(0) { - #ifdef _DEBUG - setDebugName("CIrrDeviceWin32"); - #endif + #ifdef _DEBUG + setDebugName("CIrrDeviceWin32"); + #endif - // get windows version and create OS operator - core::stringc winversion; - getWindowsVersion(winversion); - Operator = new COSOperator(winversion); - os::Printer::log(winversion.c_str(), ELL_INFORMATION); + // get windows version and create OS operator + core::stringc winversion; + getWindowsVersion(winversion); + Operator = new COSOperator(winversion); + os::Printer::log(winversion.c_str(), ELL_INFORMATION); - // get handle to exe file - HINSTANCE hInstance = GetModuleHandle(0); + // get handle to exe file + HINSTANCE hInstance = GetModuleHandle(0); - // Store original desktop mode. + // Store original desktop mode. - memset(&DesktopMode, 0, sizeof(DesktopMode)); - DesktopMode.dmSize = sizeof(DesktopMode); + memset(&DesktopMode, 0, sizeof(DesktopMode)); + DesktopMode.dmSize = sizeof(DesktopMode); - EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &DesktopMode); + EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &DesktopMode); - // create the window if we need to and we do not use the null device - if (!CreationParams.WindowId && CreationParams.DriverType != video::EDT_NULL) - { - const fschar_t* ClassName = __TEXT("CIrrDeviceWin32"); + // create the window if we need to and we do not use the null device + if (!CreationParams.WindowId && CreationParams.DriverType != video::EDT_NULL) + { + const fschar_t* ClassName = __TEXT("CIrrDeviceWin32"); - // Register Class - WNDCLASSEX wcex; - wcex.cbSize = sizeof(WNDCLASSEX); - wcex.style = CS_HREDRAW | CS_VREDRAW; - wcex.lpfnWndProc = WndProc; - wcex.cbClsExtra = 0; - wcex.cbWndExtra = 0; - wcex.hInstance = hInstance; - wcex.hIcon = NULL; - wcex.hCursor = 0; // LoadCursor(NULL, IDC_ARROW); - wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); - wcex.lpszMenuName = 0; - wcex.lpszClassName = ClassName; - wcex.hIconSm = 0; + // Register Class + WNDCLASSEX wcex; + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = hInstance; + wcex.hIcon = NULL; + wcex.hCursor = 0; // LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wcex.lpszMenuName = 0; + wcex.lpszClassName = ClassName; + wcex.hIconSm = 0; - // if there is an icon, load it - wcex.hIcon = (HICON)LoadImage(hInstance, __TEXT("irrlicht.ico"), IMAGE_ICON, 0,0, LR_LOADFROMFILE); + // if there is an icon, load it + wcex.hIcon = (HICON)LoadImage(hInstance, __TEXT("irrlicht.ico"), IMAGE_ICON, 0,0, LR_LOADFROMFILE); - RegisterClassEx(&wcex); + RegisterClassEx(&wcex); - // calculate client size + // calculate client size - RECT clientSize; - clientSize.top = 0; - clientSize.left = 0; - clientSize.right = CreationParams.WindowSize.Width; - clientSize.bottom = CreationParams.WindowSize.Height; + RECT clientSize; + clientSize.top = 0; + clientSize.left = 0; + clientSize.right = CreationParams.WindowSize.Width; + clientSize.bottom = CreationParams.WindowSize.Height; - DWORD style = WS_POPUP; + DWORD style = WS_POPUP; - if (!CreationParams.Fullscreen) - style = WS_SYSMENU | WS_BORDER | WS_CAPTION | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; + if (!CreationParams.Fullscreen) + style = WS_SYSMENU | WS_BORDER | WS_CAPTION | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; - AdjustWindowRect(&clientSize, style, FALSE); + AdjustWindowRect(&clientSize, style, FALSE); - const s32 realWidth = clientSize.right - clientSize.left; - const s32 realHeight = clientSize.bottom - clientSize.top; + const s32 realWidth = clientSize.right - clientSize.left; + const s32 realHeight = clientSize.bottom - clientSize.top; - s32 windowLeft = (GetSystemMetrics(SM_CXSCREEN) - realWidth) / 2; - s32 windowTop = (GetSystemMetrics(SM_CYSCREEN) - realHeight) / 2; + s32 windowLeft = (GetSystemMetrics(SM_CXSCREEN) - realWidth) / 2; + s32 windowTop = (GetSystemMetrics(SM_CYSCREEN) - realHeight) / 2; - if ( windowLeft < 0 ) - windowLeft = 0; - if ( windowTop < 0 ) - windowTop = 0; // make sure window menus are in screen on creation + if ( windowLeft < 0 ) + windowLeft = 0; + if ( windowTop < 0 ) + windowTop = 0; // make sure window menus are in screen on creation - if (CreationParams.Fullscreen) - { - windowLeft = 0; - windowTop = 0; - } + if (CreationParams.Fullscreen) + { + windowLeft = 0; + windowTop = 0; + } - // create window + // create window - HWnd = CreateWindow( ClassName, __TEXT(""), style, windowLeft, windowTop, - realWidth, realHeight, NULL, NULL, hInstance, NULL); - CreationParams.WindowId = HWnd; -// CreationParams.WindowSize.Width = realWidth; -// CreationParams.WindowSize.Height = realHeight; + HWnd = CreateWindow( ClassName, __TEXT(""), style, windowLeft, windowTop, + realWidth, realHeight, NULL, NULL, hInstance, NULL); + CreationParams.WindowId = HWnd; +// CreationParams.WindowSize.Width = realWidth; +// CreationParams.WindowSize.Height = realHeight; - ShowWindow(HWnd, SW_SHOWNORMAL); - UpdateWindow(HWnd); + ShowWindow(HWnd, SW_SHOWNORMAL); + UpdateWindow(HWnd); - // fix ugly ATI driver bugs. Thanks to ariaci - MoveWindow(HWnd, windowLeft, windowTop, realWidth, realHeight, TRUE); + // fix ugly ATI driver bugs. Thanks to ariaci + MoveWindow(HWnd, windowLeft, windowTop, realWidth, realHeight, TRUE); - // make sure everything gets updated to the real sizes - Resized = true; - } - else if (CreationParams.WindowId) - { - // attach external window - HWnd = static_cast(CreationParams.WindowId); - RECT r; - GetWindowRect(HWnd, &r); - CreationParams.WindowSize.Width = r.right - r.left; - CreationParams.WindowSize.Height = r.bottom - r.top; - CreationParams.Fullscreen = false; - ExternalWindow = true; - } + // make sure everything gets updated to the real sizes + Resized = true; + } + else if (CreationParams.WindowId) + { + // attach external window + HWnd = static_cast(CreationParams.WindowId); + RECT r; + GetWindowRect(HWnd, &r); + CreationParams.WindowSize.Width = r.right - r.left; + CreationParams.WindowSize.Height = r.bottom - r.top; + CreationParams.Fullscreen = false; + ExternalWindow = true; + } - // create cursor control + // create cursor control - Win32CursorControl = new CCursorControl(this, CreationParams.WindowSize, HWnd, CreationParams.Fullscreen); - CursorControl = Win32CursorControl; - JoyControl = new SJoystickWin32Control(this); + Win32CursorControl = new CCursorControl(this, CreationParams.WindowSize, HWnd, CreationParams.Fullscreen); + CursorControl = Win32CursorControl; + JoyControl = new SJoystickWin32Control(this); - // initialize doubleclicks with system values - MouseMultiClicks.DoubleClickTime = GetDoubleClickTime(); + // initialize doubleclicks with system values + MouseMultiClicks.DoubleClickTime = GetDoubleClickTime(); - // create driver + // create driver - createDriver(); + createDriver(); - if (VideoDriver) - createGUIAndScene(); + if (VideoDriver) + createGUIAndScene(); - // register environment + // register environment - SEnvMapper em; - em.irrDev = this; - em.hWnd = HWnd; - EnvMap.push_back(em); + SEnvMapper em; + em.irrDev = this; + em.hWnd = HWnd; + EnvMap.push_back(em); - // set this as active window - if (!ExternalWindow) - { - SetActiveWindow(HWnd); - SetForegroundWindow(HWnd); - } + // set this as active window + if (!ExternalWindow) + { + SetActiveWindow(HWnd); + SetForegroundWindow(HWnd); + } - // get the codepage used for keyboard input - KEYBOARD_INPUT_HKL = GetKeyboardLayout(0); - KEYBOARD_INPUT_CODEPAGE = LocaleIdToCodepage( LOWORD(KEYBOARD_INPUT_HKL) ); + // get the codepage used for keyboard input + KEYBOARD_INPUT_HKL = GetKeyboardLayout(0); + KEYBOARD_INPUT_CODEPAGE = LocaleIdToCodepage( LOWORD(KEYBOARD_INPUT_HKL) ); - // inform driver about the window size etc. - resizeIfNecessary(); + // inform driver about the window size etc. + resizeIfNecessary(); - // for reset the position of composition window - updateICPos(HWnd, 0, 0, 0); + // for reset the position of composition window + updateICPos(HWnd, 0, 0, 0); } //! destructor CIrrDeviceWin32::~CIrrDeviceWin32() { - delete JoyControl; + delete JoyControl; - // unregister environment + // unregister environment - irr::core::list::Iterator it = EnvMap.begin(); - for (; it!= EnvMap.end(); ++it) - { - if ((*it).hWnd == HWnd) - { - EnvMap.erase(it); - break; - } - } + irr::core::list::Iterator it = EnvMap.begin(); + for (; it!= EnvMap.end(); ++it) + { + if ((*it).hWnd == HWnd) + { + EnvMap.erase(it); + break; + } + } - switchToFullScreen(true); + switchToFullScreen(true); } //! create the driver void CIrrDeviceWin32::createDriver() { - switch(CreationParams.DriverType) - { - case video::EDT_DIRECT3D8: - #ifdef _IRR_COMPILE_WITH_DIRECT3D_8_ + switch(CreationParams.DriverType) + { + case video::EDT_DIRECT3D8: + #ifdef _IRR_COMPILE_WITH_DIRECT3D_8_ - VideoDriver = video::createDirectX8Driver(CreationParams, FileSystem, HWnd); + VideoDriver = video::createDirectX8Driver(CreationParams, FileSystem, HWnd); - if (!VideoDriver) - { - os::Printer::log("Could not create DIRECT3D8 Driver.", ELL_ERROR); - } - #else - os::Printer::log("DIRECT3D8 Driver was not compiled into this dll. Try another one.", ELL_ERROR); - #endif // _IRR_COMPILE_WITH_DIRECT3D_8_ + if (!VideoDriver) + { + os::Printer::log("Could not create DIRECT3D8 Driver.", ELL_ERROR); + } + #else + os::Printer::log("DIRECT3D8 Driver was not compiled into this dll. Try another one.", ELL_ERROR); + #endif // _IRR_COMPILE_WITH_DIRECT3D_8_ - break; + break; - case video::EDT_DIRECT3D9: - #ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ + case video::EDT_DIRECT3D9: + #ifdef _IRR_COMPILE_WITH_DIRECT3D_9_ - VideoDriver = video::createDirectX9Driver(CreationParams, FileSystem, HWnd); + VideoDriver = video::createDirectX9Driver(CreationParams, FileSystem, HWnd); - if (!VideoDriver) - { - os::Printer::log("Could not create DIRECT3D9 Driver.", ELL_ERROR); - } - #else - os::Printer::log("DIRECT3D9 Driver was not compiled into this dll. Try another one.", ELL_ERROR); - #endif // _IRR_COMPILE_WITH_DIRECT3D_9_ + if (!VideoDriver) + { + os::Printer::log("Could not create DIRECT3D9 Driver.", ELL_ERROR); + } + #else + os::Printer::log("DIRECT3D9 Driver was not compiled into this dll. Try another one.", ELL_ERROR); + #endif // _IRR_COMPILE_WITH_DIRECT3D_9_ - break; + break; - case video::EDT_OPENGL: + case video::EDT_OPENGL: - #ifdef _IRR_COMPILE_WITH_OPENGL_ - switchToFullScreen(); + #ifdef _IRR_COMPILE_WITH_OPENGL_ + switchToFullScreen(); - VideoDriver = video::createOpenGLDriver(CreationParams, FileSystem, this); - if (!VideoDriver) - { - os::Printer::log("Could not create OpenGL driver.", ELL_ERROR); - } - #else - os::Printer::log("OpenGL driver was not compiled in.", ELL_ERROR); - #endif - break; + VideoDriver = video::createOpenGLDriver(CreationParams, FileSystem, this); + if (!VideoDriver) + { + os::Printer::log("Could not create OpenGL driver.", ELL_ERROR); + } + #else + os::Printer::log("OpenGL driver was not compiled in.", ELL_ERROR); + #endif + break; - case video::EDT_SOFTWARE: + case video::EDT_SOFTWARE: - #ifdef _IRR_COMPILE_WITH_SOFTWARE_ - switchToFullScreen(); + #ifdef _IRR_COMPILE_WITH_SOFTWARE_ + switchToFullScreen(); - VideoDriver = video::createSoftwareDriver(CreationParams.WindowSize, CreationParams.Fullscreen, FileSystem, this); - #else - os::Printer::log("Software driver was not compiled in.", ELL_ERROR); - #endif + VideoDriver = video::createSoftwareDriver(CreationParams.WindowSize, CreationParams.Fullscreen, FileSystem, this); + #else + os::Printer::log("Software driver was not compiled in.", ELL_ERROR); + #endif - break; + break; - case video::EDT_BURNINGSVIDEO: - #ifdef _IRR_COMPILE_WITH_BURNINGSVIDEO_ - switchToFullScreen(); + case video::EDT_BURNINGSVIDEO: + #ifdef _IRR_COMPILE_WITH_BURNINGSVIDEO_ + switchToFullScreen(); - VideoDriver = video::createBurningVideoDriver(CreationParams, FileSystem, this); - #else - os::Printer::log("Burning's Video driver was not compiled in.", ELL_ERROR); - #endif - break; + VideoDriver = video::createBurningVideoDriver(CreationParams, FileSystem, this); + #else + os::Printer::log("Burning's Video driver was not compiled in.", ELL_ERROR); + #endif + break; - case video::EDT_NULL: - // create null driver - VideoDriver = video::createNullDriver(FileSystem, CreationParams.WindowSize); - break; + case video::EDT_NULL: + // create null driver + VideoDriver = video::createNullDriver(FileSystem, CreationParams.WindowSize); + break; - default: - os::Printer::log("Unable to create video driver of unknown type.", ELL_ERROR); - break; - } + default: + os::Printer::log("Unable to create video driver of unknown type.", ELL_ERROR); + break; + } } //! runs the device. Returns false if device wants to be deleted bool CIrrDeviceWin32::run() { - os::Timer::tick(); + os::Timer::tick(); - static_cast(CursorControl)->update(); + static_cast(CursorControl)->update(); - handleSystemMessages(); + handleSystemMessages(); - if (!Close) - resizeIfNecessary(); + if (!Close) + resizeIfNecessary(); - if(!Close && JoyControl) - JoyControl->pollJoysticks(); + if(!Close && JoyControl) + JoyControl->pollJoysticks(); - _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; - return !Close; + _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; + return !Close; } //! Pause the current process for the minimum time allowed only to allow other processes to execute void CIrrDeviceWin32::yield() { - Sleep(1); + Sleep(1); } //! Pause execution and let other processes to run for a specified amount of time. void CIrrDeviceWin32::sleep(u32 timeMs, bool pauseTimer) { - const bool wasStopped = Timer ? Timer->isStopped() : true; - if (pauseTimer && !wasStopped) - Timer->stop(); + const bool wasStopped = Timer ? Timer->isStopped() : true; + if (pauseTimer && !wasStopped) + Timer->stop(); - Sleep(timeMs); + Sleep(timeMs); - if (pauseTimer && !wasStopped) - Timer->start(); + if (pauseTimer && !wasStopped) + Timer->start(); } void CIrrDeviceWin32::resizeIfNecessary() { - if (!Resized || !getVideoDriver()) - return; + if (!Resized || !getVideoDriver()) + return; - RECT r; - GetClientRect(HWnd, &r); + RECT r; + GetClientRect(HWnd, &r); - char tmp[255]; + char tmp[255]; - if (r.right < 2 || r.bottom < 2) - { - sprintf(tmp, "Ignoring resize operation to (%ld %ld)", r.right, r.bottom); - os::Printer::log(tmp); - } - else - { - sprintf(tmp, "Resizing window (%ld %ld)", r.right, r.bottom); - os::Printer::log(tmp); + if (r.right < 2 || r.bottom < 2) + { + sprintf(tmp, "Ignoring resize operation to (%ld %ld)", r.right, r.bottom); + os::Printer::log(tmp); + } + else + { + sprintf(tmp, "Resizing window (%ld %ld)", r.right, r.bottom); + os::Printer::log(tmp); - getVideoDriver()->OnResize(irr::core::dimension2du((u32)r.right, (u32)r.bottom)); - getWin32CursorControl()->OnResize(getVideoDriver()->getScreenSize()); - } + getVideoDriver()->OnResize(irr::core::dimension2du((u32)r.right, (u32)r.bottom)); + getWin32CursorControl()->OnResize(getVideoDriver()->getScreenSize()); + } - Resized = false; + Resized = false; } //! sets the caption of the window void CIrrDeviceWin32::setWindowCaption(const wchar_t* text) { - // We use SendMessage instead of SetText to ensure proper - // function even in cases where the HWND was created in a different thread - DWORD_PTR dwResult; - SendMessageTimeoutW(HWnd, WM_SETTEXT, 0, - reinterpret_cast(text), - SMTO_ABORTIFHUNG, 2000, &dwResult); + // We use SendMessage instead of SetText to ensure proper + // function even in cases where the HWND was created in a different thread + DWORD_PTR dwResult; + SendMessageTimeoutW(HWnd, WM_SETTEXT, 0, + reinterpret_cast(text), + SMTO_ABORTIFHUNG, 2000, &dwResult); } //! presents a surface in the client area bool CIrrDeviceWin32::present(video::IImage* image, void* windowId, core::rect* src) { - HWND hwnd = HWnd; - if ( windowId ) - hwnd = reinterpret_cast(windowId); + HWND hwnd = HWnd; + if ( windowId ) + hwnd = reinterpret_cast(windowId); - HDC dc = GetDC(hwnd); + HDC dc = GetDC(hwnd); - if ( dc ) - { - RECT rect; - GetClientRect(hwnd, &rect); - const void* memory = (const void *)image->lock(); + if ( dc ) + { + RECT rect; + GetClientRect(hwnd, &rect); + const void* memory = (const void *)image->lock(); - BITMAPV4HEADER bi; - ZeroMemory (&bi, sizeof(bi)); - bi.bV4Size = sizeof(BITMAPINFOHEADER); - bi.bV4BitCount = (WORD)image->getBitsPerPixel(); - bi.bV4Planes = 1; - bi.bV4Width = image->getDimension().Width; - bi.bV4Height = -((s32)image->getDimension().Height); - bi.bV4V4Compression = BI_BITFIELDS; - bi.bV4AlphaMask = image->getAlphaMask(); - bi.bV4RedMask = image->getRedMask(); - bi.bV4GreenMask = image->getGreenMask(); - bi.bV4BlueMask = image->getBlueMask(); + BITMAPV4HEADER bi; + ZeroMemory (&bi, sizeof(bi)); + bi.bV4Size = sizeof(BITMAPINFOHEADER); + bi.bV4BitCount = (WORD)image->getBitsPerPixel(); + bi.bV4Planes = 1; + bi.bV4Width = image->getDimension().Width; + bi.bV4Height = -((s32)image->getDimension().Height); + bi.bV4V4Compression = BI_BITFIELDS; + bi.bV4AlphaMask = image->getAlphaMask(); + bi.bV4RedMask = image->getRedMask(); + bi.bV4GreenMask = image->getGreenMask(); + bi.bV4BlueMask = image->getBlueMask(); - if ( src ) - { - StretchDIBits(dc, 0,0, rect.right, rect.bottom, - src->UpperLeftCorner.X, src->UpperLeftCorner.Y, - src->getWidth(), src->getHeight(), - memory, (const BITMAPINFO*)(&bi), DIB_RGB_COLORS, SRCCOPY); - } - else - { - StretchDIBits(dc, 0,0, rect.right, rect.bottom, - 0, 0, image->getDimension().Width, image->getDimension().Height, - memory, (const BITMAPINFO*)(&bi), DIB_RGB_COLORS, SRCCOPY); - } + if ( src ) + { + StretchDIBits(dc, 0,0, rect.right, rect.bottom, + src->UpperLeftCorner.X, src->UpperLeftCorner.Y, + src->getWidth(), src->getHeight(), + memory, (const BITMAPINFO*)(&bi), DIB_RGB_COLORS, SRCCOPY); + } + else + { + StretchDIBits(dc, 0,0, rect.right, rect.bottom, + 0, 0, image->getDimension().Width, image->getDimension().Height, + memory, (const BITMAPINFO*)(&bi), DIB_RGB_COLORS, SRCCOPY); + } - image->unlock(); + image->unlock(); - ReleaseDC(hwnd, dc); - } - return true; + ReleaseDC(hwnd, dc); + } + return true; } //! notifies the device that it should close itself void CIrrDeviceWin32::closeDevice() { - MSG msg; - PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE); - PostQuitMessage(0); - PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE); - if (!ExternalWindow) - { - DestroyWindow(HWnd); - const fschar_t* ClassName = __TEXT("CIrrDeviceWin32"); - HINSTANCE hInstance = GetModuleHandle(0); - UnregisterClass(ClassName, hInstance); - } - Close=true; + MSG msg; + PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE); + PostQuitMessage(0); + PeekMessage(&msg, NULL, WM_QUIT, WM_QUIT, PM_REMOVE); + if (!ExternalWindow) + { + DestroyWindow(HWnd); + const fschar_t* ClassName = __TEXT("CIrrDeviceWin32"); + HINSTANCE hInstance = GetModuleHandle(0); + UnregisterClass(ClassName, hInstance); + } + Close=true; } //! returns if window is active. if not, nothing needs to be drawn bool CIrrDeviceWin32::isWindowActive() const { - _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; - return (GetActiveWindow() == HWnd); + _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; + return (GetActiveWindow() == HWnd); } //! returns if window has focus bool CIrrDeviceWin32::isWindowFocused() const { - bool ret = (GetFocus() == HWnd); - _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; - return ret; + bool ret = (GetFocus() == HWnd); + _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; + return ret; } //! returns if window is minimized bool CIrrDeviceWin32::isWindowMinimized() const { - WINDOWPLACEMENT plc; - plc.length=sizeof(WINDOWPLACEMENT); - bool ret=false; - if (GetWindowPlacement(HWnd,&plc)) - ret=(plc.showCmd & SW_SHOWMINIMIZED)!=0; - _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; - return ret; + WINDOWPLACEMENT plc; + plc.length=sizeof(WINDOWPLACEMENT); + bool ret=false; + if (GetWindowPlacement(HWnd,&plc)) + ret=(plc.showCmd & SW_SHOWMINIMIZED)!=0; + _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; + return ret; } //! switches to fullscreen bool CIrrDeviceWin32::switchToFullScreen(bool reset) { - if (!CreationParams.Fullscreen) - return true; + if (!CreationParams.Fullscreen) + return true; - if (reset) - { - if (ChangedToFullScreen) - { - return (ChangeDisplaySettings(&DesktopMode,0)==DISP_CHANGE_SUCCESSFUL); - } - else - return true; - } + if (reset) + { + if (ChangedToFullScreen) + { + return (ChangeDisplaySettings(&DesktopMode,0)==DISP_CHANGE_SUCCESSFUL); + } + else + return true; + } - // use default values from current setting + // use default values from current setting - DEVMODE dm; - memset(&dm, 0, sizeof(dm)); - dm.dmSize = sizeof(dm); + DEVMODE dm; + memset(&dm, 0, sizeof(dm)); + dm.dmSize = sizeof(dm); - EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm); - dm.dmPelsWidth = CreationParams.WindowSize.Width; - dm.dmPelsHeight = CreationParams.WindowSize.Height; - dm.dmBitsPerPel = CreationParams.Bits; - dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY; + EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm); + dm.dmPelsWidth = CreationParams.WindowSize.Width; + dm.dmPelsHeight = CreationParams.WindowSize.Height; + dm.dmBitsPerPel = CreationParams.Bits; + dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY; - LONG res = ChangeDisplaySettings(&dm, CDS_FULLSCREEN); - if (res != DISP_CHANGE_SUCCESSFUL) - { // try again without forcing display frequency - dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; - res = ChangeDisplaySettings(&dm, CDS_FULLSCREEN); - } + LONG res = ChangeDisplaySettings(&dm, CDS_FULLSCREEN); + if (res != DISP_CHANGE_SUCCESSFUL) + { // try again without forcing display frequency + dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; + res = ChangeDisplaySettings(&dm, CDS_FULLSCREEN); + } - bool ret = false; - switch(res) - { - case DISP_CHANGE_SUCCESSFUL: - ChangedToFullScreen = true; - ret = true; - break; - case DISP_CHANGE_RESTART: - os::Printer::log("Switch to fullscreen: The computer must be restarted in order for the graphics mode to work.", ELL_ERROR); - break; - case DISP_CHANGE_BADFLAGS: - os::Printer::log("Switch to fullscreen: An invalid set of flags was passed in.", ELL_ERROR); - break; - case DISP_CHANGE_BADPARAM: - os::Printer::log("Switch to fullscreen: An invalid parameter was passed in. This can include an invalid flag or combination of flags.", ELL_ERROR); - break; - case DISP_CHANGE_FAILED: - os::Printer::log("Switch to fullscreen: The display driver failed the specified graphics mode.", ELL_ERROR); - break; - case DISP_CHANGE_BADMODE: - os::Printer::log("Switch to fullscreen: The graphics mode is not supported.", ELL_ERROR); - break; - default: - os::Printer::log("An unknown error occured while changing to fullscreen.", ELL_ERROR); - break; - } - _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; - return ret; + bool ret = false; + switch(res) + { + case DISP_CHANGE_SUCCESSFUL: + ChangedToFullScreen = true; + ret = true; + break; + case DISP_CHANGE_RESTART: + os::Printer::log("Switch to fullscreen: The computer must be restarted in order for the graphics mode to work.", ELL_ERROR); + break; + case DISP_CHANGE_BADFLAGS: + os::Printer::log("Switch to fullscreen: An invalid set of flags was passed in.", ELL_ERROR); + break; + case DISP_CHANGE_BADPARAM: + os::Printer::log("Switch to fullscreen: An invalid parameter was passed in. This can include an invalid flag or combination of flags.", ELL_ERROR); + break; + case DISP_CHANGE_FAILED: + os::Printer::log("Switch to fullscreen: The display driver failed the specified graphics mode.", ELL_ERROR); + break; + case DISP_CHANGE_BADMODE: + os::Printer::log("Switch to fullscreen: The graphics mode is not supported.", ELL_ERROR); + break; + default: + os::Printer::log("An unknown error occured while changing to fullscreen.", ELL_ERROR); + break; + } + _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX; + return ret; } //! returns the win32 cursor control CIrrDeviceWin32::CCursorControl* CIrrDeviceWin32::getWin32CursorControl() { - return Win32CursorControl; + return Win32CursorControl; } @@ -1553,27 +1553,27 @@ CIrrDeviceWin32::CCursorControl* CIrrDeviceWin32::getWin32CursorControl() //! by the gfx adapter. video::IVideoModeList* CIrrDeviceWin32::getVideoModeList() { - if (!VideoModeList.getVideoModeCount()) - { - // enumerate video modes. - DWORD i=0; - DEVMODE mode; - memset(&mode, 0, sizeof(mode)); - mode.dmSize = sizeof(mode); + if (!VideoModeList.getVideoModeCount()) + { + // enumerate video modes. + DWORD i=0; + DEVMODE mode; + memset(&mode, 0, sizeof(mode)); + mode.dmSize = sizeof(mode); - while (EnumDisplaySettings(NULL, i, &mode)) - { - VideoModeList.addMode(core::dimension2d(mode.dmPelsWidth, mode.dmPelsHeight), - mode.dmBitsPerPel); + while (EnumDisplaySettings(NULL, i, &mode)) + { + VideoModeList.addMode(core::dimension2d(mode.dmPelsWidth, mode.dmPelsHeight), + mode.dmBitsPerPel); - ++i; - } + ++i; + } - if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &mode)) - VideoModeList.setDesktop(mode.dmBitsPerPel, core::dimension2d(mode.dmPelsWidth, mode.dmPelsHeight)); - } + if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &mode)) + VideoModeList.setDesktop(mode.dmBitsPerPel, core::dimension2d(mode.dmPelsWidth, mode.dmPelsHeight)); + } - return &VideoModeList; + return &VideoModeList; } typedef BOOL (WINAPI *PGPI)(DWORD, DWORD, DWORD, DWORD, PDWORD); @@ -1581,356 +1581,356 @@ typedef BOOL (WINAPI *PGPI)(DWORD, DWORD, DWORD, DWORD, PDWORD); // depending on the SDK version and compilers some defines might be available // or not #ifndef PRODUCT_ULTIMATE -#define PRODUCT_ULTIMATE 0x00000001 -#define PRODUCT_HOME_BASIC 0x00000002 -#define PRODUCT_HOME_PREMIUM 0x00000003 -#define PRODUCT_ENTERPRISE 0x00000004 -#define PRODUCT_HOME_BASIC_N 0x00000005 -#define PRODUCT_BUSINESS 0x00000006 -#define PRODUCT_STARTER 0x0000000B +#define PRODUCT_ULTIMATE 0x00000001 +#define PRODUCT_HOME_BASIC 0x00000002 +#define PRODUCT_HOME_PREMIUM 0x00000003 +#define PRODUCT_ENTERPRISE 0x00000004 +#define PRODUCT_HOME_BASIC_N 0x00000005 +#define PRODUCT_BUSINESS 0x00000006 +#define PRODUCT_STARTER 0x0000000B #endif #ifndef PRODUCT_ULTIMATE_N -#define PRODUCT_BUSINESS_N 0x00000010 -#define PRODUCT_HOME_PREMIUM_N 0x0000001A -#define PRODUCT_ENTERPRISE_N 0x0000001B -#define PRODUCT_ULTIMATE_N 0x0000001C +#define PRODUCT_BUSINESS_N 0x00000010 +#define PRODUCT_HOME_PREMIUM_N 0x0000001A +#define PRODUCT_ENTERPRISE_N 0x0000001B +#define PRODUCT_ULTIMATE_N 0x0000001C #endif #ifndef PRODUCT_STARTER_N -#define PRODUCT_STARTER_N 0x0000002F +#define PRODUCT_STARTER_N 0x0000002F #endif #ifndef PRODUCT_PROFESSIONAL -#define PRODUCT_PROFESSIONAL 0x00000030 -#define PRODUCT_PROFESSIONAL_N 0x00000031 +#define PRODUCT_PROFESSIONAL 0x00000030 +#define PRODUCT_PROFESSIONAL_N 0x00000031 #endif #ifndef PRODUCT_ULTIMATE_E -#define PRODUCT_STARTER_E 0x00000042 -#define PRODUCT_HOME_BASIC_E 0x00000043 -#define PRODUCT_HOME_PREMIUM_E 0x00000044 -#define PRODUCT_PROFESSIONAL_E 0x00000045 -#define PRODUCT_ENTERPRISE_E 0x00000046 -#define PRODUCT_ULTIMATE_E 0x00000047 +#define PRODUCT_STARTER_E 0x00000042 +#define PRODUCT_HOME_BASIC_E 0x00000043 +#define PRODUCT_HOME_PREMIUM_E 0x00000044 +#define PRODUCT_PROFESSIONAL_E 0x00000045 +#define PRODUCT_ENTERPRISE_E 0x00000046 +#define PRODUCT_ULTIMATE_E 0x00000047 #endif void CIrrDeviceWin32::getWindowsVersion(core::stringc& out) { - OSVERSIONINFOEX osvi; - PGPI pGPI; - BOOL bOsVersionInfoEx; + OSVERSIONINFOEX osvi; + PGPI pGPI; + BOOL bOsVersionInfoEx; - ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO*) &osvi); - if (!bOsVersionInfoEx) - { - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - if (! GetVersionEx((OSVERSIONINFO *) &osvi)) - return; - } + bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO*) &osvi); + if (!bOsVersionInfoEx) + { + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + if (! GetVersionEx((OSVERSIONINFO *) &osvi)) + return; + } - switch (osvi.dwPlatformId) - { - case VER_PLATFORM_WIN32_NT: - if (osvi.dwMajorVersion <= 4) - out.append("Microsoft Windows NT "); - else - if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) - out.append("Microsoft Windows 2000 "); - else - if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) - out.append("Microsoft Windows XP "); - else - if (osvi.dwMajorVersion == 6 ) - { - if (osvi.dwMinorVersion == 0) - { - if (osvi.wProductType == VER_NT_WORKSTATION) - out.append("Microsoft Windows Vista "); - else - out.append("Microsoft Windows Server 2008 "); - } - else if (osvi.dwMinorVersion == 1) - { - if (osvi.wProductType == VER_NT_WORKSTATION) - out.append("Microsoft Windows 7 "); - else - out.append("Microsoft Windows Server 2008 R2 "); - } - else if (osvi.dwMinorVersion == 2) - { - if (osvi.wProductType == VER_NT_WORKSTATION) - out.append("Microsoft Windows 8 "); - else - out.append("Microsoft Windows Server 2012 "); - } - else if (osvi.dwMinorVersion == 3) - { - if (osvi.wProductType == VER_NT_WORKSTATION) - out.append("Microsoft Windows 8.1 "); - else - out.append("Microsoft Windows Server 2012 R2 "); - } - } + switch (osvi.dwPlatformId) + { + case VER_PLATFORM_WIN32_NT: + if (osvi.dwMajorVersion <= 4) + out.append("Microsoft Windows NT "); + else + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) + out.append("Microsoft Windows 2000 "); + else + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) + out.append("Microsoft Windows XP "); + else + if (osvi.dwMajorVersion == 6 ) + { + if (osvi.dwMinorVersion == 0) + { + if (osvi.wProductType == VER_NT_WORKSTATION) + out.append("Microsoft Windows Vista "); + else + out.append("Microsoft Windows Server 2008 "); + } + else if (osvi.dwMinorVersion == 1) + { + if (osvi.wProductType == VER_NT_WORKSTATION) + out.append("Microsoft Windows 7 "); + else + out.append("Microsoft Windows Server 2008 R2 "); + } + else if (osvi.dwMinorVersion == 2) + { + if (osvi.wProductType == VER_NT_WORKSTATION) + out.append("Microsoft Windows 8 "); + else + out.append("Microsoft Windows Server 2012 "); + } + else if (osvi.dwMinorVersion == 3) + { + if (osvi.wProductType == VER_NT_WORKSTATION) + out.append("Microsoft Windows 8.1 "); + else + out.append("Microsoft Windows Server 2012 R2 "); + } + } - if (bOsVersionInfoEx) - { - if (osvi.dwMajorVersion == 6) - { - DWORD dwType; - pGPI = (PGPI)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetProductInfo"); - pGPI(osvi.dwMajorVersion, osvi.dwMinorVersion, 0, 0, &dwType); + if (bOsVersionInfoEx) + { + if (osvi.dwMajorVersion == 6) + { + DWORD dwType; + pGPI = (PGPI)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetProductInfo"); + pGPI(osvi.dwMajorVersion, osvi.dwMinorVersion, 0, 0, &dwType); - switch (dwType) - { - case PRODUCT_ULTIMATE: - case PRODUCT_ULTIMATE_E: - case PRODUCT_ULTIMATE_N: - out.append("Ultimate Edition "); - break; - case PRODUCT_PROFESSIONAL: - case PRODUCT_PROFESSIONAL_E: - case PRODUCT_PROFESSIONAL_N: - out.append("Professional Edition "); - break; - case PRODUCT_HOME_BASIC: - case PRODUCT_HOME_BASIC_E: - case PRODUCT_HOME_BASIC_N: - out.append("Home Basic Edition "); - break; - case PRODUCT_HOME_PREMIUM: - case PRODUCT_HOME_PREMIUM_E: - case PRODUCT_HOME_PREMIUM_N: - out.append("Home Premium Edition "); - break; - case PRODUCT_ENTERPRISE: - case PRODUCT_ENTERPRISE_E: - case PRODUCT_ENTERPRISE_N: - out.append("Enterprise Edition "); - break; - case PRODUCT_BUSINESS: - case PRODUCT_BUSINESS_N: - out.append("Business Edition "); - break; - case PRODUCT_STARTER: - case PRODUCT_STARTER_E: - case PRODUCT_STARTER_N: - out.append("Starter Edition "); - break; - } - } + switch (dwType) + { + case PRODUCT_ULTIMATE: + case PRODUCT_ULTIMATE_E: + case PRODUCT_ULTIMATE_N: + out.append("Ultimate Edition "); + break; + case PRODUCT_PROFESSIONAL: + case PRODUCT_PROFESSIONAL_E: + case PRODUCT_PROFESSIONAL_N: + out.append("Professional Edition "); + break; + case PRODUCT_HOME_BASIC: + case PRODUCT_HOME_BASIC_E: + case PRODUCT_HOME_BASIC_N: + out.append("Home Basic Edition "); + break; + case PRODUCT_HOME_PREMIUM: + case PRODUCT_HOME_PREMIUM_E: + case PRODUCT_HOME_PREMIUM_N: + out.append("Home Premium Edition "); + break; + case PRODUCT_ENTERPRISE: + case PRODUCT_ENTERPRISE_E: + case PRODUCT_ENTERPRISE_N: + out.append("Enterprise Edition "); + break; + case PRODUCT_BUSINESS: + case PRODUCT_BUSINESS_N: + out.append("Business Edition "); + break; + case PRODUCT_STARTER: + case PRODUCT_STARTER_E: + case PRODUCT_STARTER_N: + out.append("Starter Edition "); + break; + } + } #ifdef VER_SUITE_ENTERPRISE - else - if (osvi.wProductType == VER_NT_WORKSTATION) - { + else + if (osvi.wProductType == VER_NT_WORKSTATION) + { #ifndef __BORLANDC__ - if( osvi.wSuiteMask & VER_SUITE_PERSONAL ) - out.append("Personal "); - else - out.append("Professional "); + if( osvi.wSuiteMask & VER_SUITE_PERSONAL ) + out.append("Personal "); + else + out.append("Professional "); #endif - } - else if (osvi.wProductType == VER_NT_SERVER) - { - if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) - out.append("DataCenter Server "); - else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) - out.append("Advanced Server "); - else - out.append("Server "); - } + } + else if (osvi.wProductType == VER_NT_SERVER) + { + if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) + out.append("DataCenter Server "); + else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) + out.append("Advanced Server "); + else + out.append("Server "); + } #endif - } - else - { - HKEY hKey; - char szProductType[80]; - DWORD dwBufLen; + } + else + { + HKEY hKey; + char szProductType[80]; + DWORD dwBufLen; - RegOpenKeyEx( HKEY_LOCAL_MACHINE, - __TEXT("SYSTEM\\CurrentControlSet\\Control\\ProductOptions"), - 0, KEY_QUERY_VALUE, &hKey ); - RegQueryValueEx( hKey, __TEXT("ProductType"), NULL, NULL, - (LPBYTE) szProductType, &dwBufLen); - RegCloseKey( hKey ); + RegOpenKeyEx( HKEY_LOCAL_MACHINE, + __TEXT("SYSTEM\\CurrentControlSet\\Control\\ProductOptions"), + 0, KEY_QUERY_VALUE, &hKey ); + RegQueryValueEx( hKey, __TEXT("ProductType"), NULL, NULL, + (LPBYTE) szProductType, &dwBufLen); + RegCloseKey( hKey ); - if (_strcmpi( "WINNT", szProductType) == 0 ) - out.append("Professional "); - if (_strcmpi( "LANMANNT", szProductType) == 0) - out.append("Server "); - if (_strcmpi( "SERVERNT", szProductType) == 0) - out.append("Advanced Server "); - } + if (_strcmpi( "WINNT", szProductType) == 0 ) + out.append("Professional "); + if (_strcmpi( "LANMANNT", szProductType) == 0) + out.append("Server "); + if (_strcmpi( "SERVERNT", szProductType) == 0) + out.append("Advanced Server "); + } - // Display version, service pack (if any), and build number. + // Display version, service pack (if any), and build number. - char tmp[255]; + char tmp[255]; - if (osvi.dwMajorVersion <= 4 ) - { - sprintf(tmp, "version %ld.%ld %s (Build %ld)", - osvi.dwMajorVersion, - osvi.dwMinorVersion, - irr::core::stringc(osvi.szCSDVersion).c_str(), - osvi.dwBuildNumber & 0xFFFF); - } - else - { - sprintf(tmp, "%s (Build %ld)", irr::core::stringc(osvi.szCSDVersion).c_str(), - osvi.dwBuildNumber & 0xFFFF); - } + if (osvi.dwMajorVersion <= 4 ) + { + sprintf(tmp, "version %ld.%ld %s (Build %ld)", + osvi.dwMajorVersion, + osvi.dwMinorVersion, + irr::core::stringc(osvi.szCSDVersion).c_str(), + osvi.dwBuildNumber & 0xFFFF); + } + else + { + sprintf(tmp, "%s (Build %ld)", irr::core::stringc(osvi.szCSDVersion).c_str(), + osvi.dwBuildNumber & 0xFFFF); + } - out.append(tmp); - break; + out.append(tmp); + break; - case VER_PLATFORM_WIN32_WINDOWS: + case VER_PLATFORM_WIN32_WINDOWS: - if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0) - { - out.append("Microsoft Windows 95 "); - if ( osvi.szCSDVersion[1] == 'C' || osvi.szCSDVersion[1] == 'B' ) - out.append("OSR2 " ); - } + if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0) + { + out.append("Microsoft Windows 95 "); + if ( osvi.szCSDVersion[1] == 'C' || osvi.szCSDVersion[1] == 'B' ) + out.append("OSR2 " ); + } - if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10) - { - out.append("Microsoft Windows 98 "); - if ( osvi.szCSDVersion[1] == 'A' ) - out.append( "SE " ); - } + if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10) + { + out.append("Microsoft Windows 98 "); + if ( osvi.szCSDVersion[1] == 'A' ) + out.append( "SE " ); + } - if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90) - out.append("Microsoft Windows Me "); + if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90) + out.append("Microsoft Windows Me "); - break; + break; - case VER_PLATFORM_WIN32s: - out.append("Microsoft Win32s "); - break; - } + case VER_PLATFORM_WIN32s: + out.append("Microsoft Win32s "); + break; + } } //! Notifies the device, that it has been resized void CIrrDeviceWin32::OnResized() { - Resized = true; + Resized = true; } //! Sets if the window should be resizable in windowed mode. void CIrrDeviceWin32::setResizable(bool resize) { - if (ExternalWindow || !getVideoDriver() || CreationParams.Fullscreen) - return; + if (ExternalWindow || !getVideoDriver() || CreationParams.Fullscreen) + return; - LONG_PTR style = WS_POPUP; + LONG_PTR style = WS_POPUP; - if (!resize) - style = WS_SYSMENU | WS_BORDER | WS_CAPTION | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; - else - style = WS_THICKFRAME | WS_SYSMENU | WS_CAPTION | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX; + if (!resize) + style = WS_SYSMENU | WS_BORDER | WS_CAPTION | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; + else + style = WS_THICKFRAME | WS_SYSMENU | WS_CAPTION | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX; - if (!SetWindowLongPtr(HWnd, GWL_STYLE, style)) - os::Printer::log("Could not change window style."); + if (!SetWindowLongPtr(HWnd, GWL_STYLE, style)) + os::Printer::log("Could not change window style."); - RECT clientSize; - clientSize.top = 0; - clientSize.left = 0; - clientSize.right = getVideoDriver()->getScreenSize().Width; - clientSize.bottom = getVideoDriver()->getScreenSize().Height; + RECT clientSize; + clientSize.top = 0; + clientSize.left = 0; + clientSize.right = getVideoDriver()->getScreenSize().Width; + clientSize.bottom = getVideoDriver()->getScreenSize().Height; - AdjustWindowRect(&clientSize, style, FALSE); + AdjustWindowRect(&clientSize, style, FALSE); - const s32 realWidth = clientSize.right - clientSize.left; - const s32 realHeight = clientSize.bottom - clientSize.top; + const s32 realWidth = clientSize.right - clientSize.left; + const s32 realHeight = clientSize.bottom - clientSize.top; - const s32 windowLeft = (GetSystemMetrics(SM_CXSCREEN) - realWidth) / 2; - const s32 windowTop = (GetSystemMetrics(SM_CYSCREEN) - realHeight) / 2; + const s32 windowLeft = (GetSystemMetrics(SM_CXSCREEN) - realWidth) / 2; + const s32 windowTop = (GetSystemMetrics(SM_CYSCREEN) - realHeight) / 2; - SetWindowPos(HWnd, HWND_TOP, windowLeft, windowTop, realWidth, realHeight, - SWP_FRAMECHANGED | SWP_NOMOVE | SWP_SHOWWINDOW); + SetWindowPos(HWnd, HWND_TOP, windowLeft, windowTop, realWidth, realHeight, + SWP_FRAMECHANGED | SWP_NOMOVE | SWP_SHOWWINDOW); - static_cast(CursorControl)->updateBorderSize(CreationParams.Fullscreen, resize); + static_cast(CursorControl)->updateBorderSize(CreationParams.Fullscreen, resize); } //! Minimizes the window. void CIrrDeviceWin32::minimizeWindow() { - WINDOWPLACEMENT wndpl; - wndpl.length = sizeof(WINDOWPLACEMENT); - GetWindowPlacement(HWnd, &wndpl); - wndpl.showCmd = SW_SHOWMINNOACTIVE; - SetWindowPlacement(HWnd, &wndpl); + WINDOWPLACEMENT wndpl; + wndpl.length = sizeof(WINDOWPLACEMENT); + GetWindowPlacement(HWnd, &wndpl); + wndpl.showCmd = SW_SHOWMINNOACTIVE; + SetWindowPlacement(HWnd, &wndpl); } //! Maximizes the window. void CIrrDeviceWin32::maximizeWindow() { - WINDOWPLACEMENT wndpl; - wndpl.length = sizeof(WINDOWPLACEMENT); - GetWindowPlacement(HWnd, &wndpl); - wndpl.showCmd = SW_SHOWMAXIMIZED; - SetWindowPlacement(HWnd, &wndpl); + WINDOWPLACEMENT wndpl; + wndpl.length = sizeof(WINDOWPLACEMENT); + GetWindowPlacement(HWnd, &wndpl); + wndpl.showCmd = SW_SHOWMAXIMIZED; + SetWindowPlacement(HWnd, &wndpl); } //! Restores the window to its original size. void CIrrDeviceWin32::restoreWindow() { - WINDOWPLACEMENT wndpl; - wndpl.length = sizeof(WINDOWPLACEMENT); - GetWindowPlacement(HWnd, &wndpl); - wndpl.showCmd = SW_SHOWNORMAL; - SetWindowPlacement(HWnd, &wndpl); + WINDOWPLACEMENT wndpl; + wndpl.length = sizeof(WINDOWPLACEMENT); + GetWindowPlacement(HWnd, &wndpl); + wndpl.showCmd = SW_SHOWNORMAL; + SetWindowPlacement(HWnd, &wndpl); } bool CIrrDeviceWin32::activateJoysticks(core::array & joystickInfo) { - if (JoyControl) - return JoyControl->activateJoysticks(joystickInfo); - else - return false; + if (JoyControl) + return JoyControl->activateJoysticks(joystickInfo); + else + return false; } //! Set the current Gamma Value for the Display bool CIrrDeviceWin32::setGammaRamp( f32 red, f32 green, f32 blue, f32 brightness, f32 contrast ) { - bool r; - u16 ramp[3][256]; + bool r; + u16 ramp[3][256]; - calculateGammaRamp( ramp[0], red, brightness, contrast ); - calculateGammaRamp( ramp[1], green, brightness, contrast ); - calculateGammaRamp( ramp[2], blue, brightness, contrast ); + calculateGammaRamp( ramp[0], red, brightness, contrast ); + calculateGammaRamp( ramp[1], green, brightness, contrast ); + calculateGammaRamp( ramp[2], blue, brightness, contrast ); - HDC dc = GetDC(0); - r = SetDeviceGammaRamp ( dc, ramp ) == TRUE; - ReleaseDC(HWnd, dc); - return r; + HDC dc = GetDC(0); + r = SetDeviceGammaRamp ( dc, ramp ) == TRUE; + ReleaseDC(HWnd, dc); + return r; } //! Get the current Gamma Value for the Display bool CIrrDeviceWin32::getGammaRamp( f32 &red, f32 &green, f32 &blue, f32 &brightness, f32 &contrast ) { - bool r; - u16 ramp[3][256]; + bool r; + u16 ramp[3][256]; - HDC dc = GetDC(0); - r = GetDeviceGammaRamp ( dc, ramp ) == TRUE; - ReleaseDC(HWnd, dc); + HDC dc = GetDC(0); + r = GetDeviceGammaRamp ( dc, ramp ) == TRUE; + ReleaseDC(HWnd, dc); - if ( r ) - { - calculateGammaFromRamp(red, ramp[0]); - calculateGammaFromRamp(green, ramp[1]); - calculateGammaFromRamp(blue, ramp[2]); - } + if ( r ) + { + calculateGammaFromRamp(red, ramp[0]); + calculateGammaFromRamp(green, ramp[1]); + calculateGammaFromRamp(blue, ramp[2]); + } - brightness = 0.f; - contrast = 0.f; + brightness = 0.f; + contrast = 0.f; - return r; + return r; } @@ -1938,278 +1938,278 @@ bool CIrrDeviceWin32::getGammaRamp( f32 &red, f32 &green, f32 &blue, f32 &bright //! Process system events void CIrrDeviceWin32::handleSystemMessages() { - MSG msg; + MSG msg; - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) - { - // No message translation because we don't use WM_CHAR and it would conflict with our - // deadkey handling. + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + // No message translation because we don't use WM_CHAR and it would conflict with our + // deadkey handling. - TranslateMessage(&msg); + TranslateMessage(&msg); - if (ExternalWindow && msg.hwnd == HWnd) - WndProc(HWnd, msg.message, msg.wParam, msg.lParam); - else - DispatchMessage(&msg); + if (ExternalWindow && msg.hwnd == HWnd) + WndProc(HWnd, msg.message, msg.wParam, msg.lParam); + else + DispatchMessage(&msg); - if (msg.message == WM_QUIT) - Close = true; - } + if (msg.message == WM_QUIT) + Close = true; + } } //! Remove all messages pending in the system message loop void CIrrDeviceWin32::clearSystemMessages() { - MSG msg; - while (PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)) - {} - while (PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE)) - {} + MSG msg; + while (PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE)) + {} + while (PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE)) + {} } // shows last error in a messagebox to help internal debugging. void CIrrDeviceWin32::ReportLastWinApiError() { - // (based on code from ovidiucucu from http://www.codeguru.com/forum/showthread.php?t=318721) - LPCTSTR pszCaption = __TEXT("Windows SDK Error Report"); - DWORD dwError = GetLastError(); + // (based on code from ovidiucucu from http://www.codeguru.com/forum/showthread.php?t=318721) + LPCTSTR pszCaption = __TEXT("Windows SDK Error Report"); + DWORD dwError = GetLastError(); - if(NOERROR == dwError) - { - MessageBox(NULL, __TEXT("No error"), pszCaption, MB_OK); - } - else - { - const DWORD dwFormatControl = FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_IGNORE_INSERTS | - FORMAT_MESSAGE_FROM_SYSTEM; + if(NOERROR == dwError) + { + MessageBox(NULL, __TEXT("No error"), pszCaption, MB_OK); + } + else + { + const DWORD dwFormatControl = FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM; - LPVOID pTextBuffer = NULL; - DWORD dwCount = FormatMessage(dwFormatControl, - NULL, - dwError, - 0, - (LPTSTR) &pTextBuffer, - 0, - NULL); - if(0 != dwCount) - { - MessageBox(NULL, (LPCTSTR)pTextBuffer, pszCaption, MB_OK|MB_ICONERROR); - LocalFree(pTextBuffer); - } - else - { - MessageBox(NULL, __TEXT("Unknown error"), pszCaption, MB_OK|MB_ICONERROR); - } - } + LPVOID pTextBuffer = NULL; + DWORD dwCount = FormatMessage(dwFormatControl, + NULL, + dwError, + 0, + (LPTSTR) &pTextBuffer, + 0, + NULL); + if(0 != dwCount) + { + MessageBox(NULL, (LPCTSTR)pTextBuffer, pszCaption, MB_OK|MB_ICONERROR); + LocalFree(pTextBuffer); + } + else + { + MessageBox(NULL, __TEXT("Unknown error"), pszCaption, MB_OK|MB_ICONERROR); + } + } } // Convert an Irrlicht texture to a Windows cursor // Based on http://www.codeguru.com/cpp/w-p/win32/cursors/article.php/c4529/ HCURSOR CIrrDeviceWin32::TextureToCursor(HWND hwnd, irr::video::ITexture * tex, const core::rect& sourceRect, const core::position2d &hotspot) { - // - // create the bitmaps needed for cursors from the texture + // + // create the bitmaps needed for cursors from the texture - HDC dc = GetDC(hwnd); - HDC andDc = CreateCompatibleDC(dc); - HDC xorDc = CreateCompatibleDC(dc); - HBITMAP andBitmap = CreateCompatibleBitmap(dc, sourceRect.getWidth(), sourceRect.getHeight()); - HBITMAP xorBitmap = CreateCompatibleBitmap(dc, sourceRect.getWidth(), sourceRect.getHeight()); + HDC dc = GetDC(hwnd); + HDC andDc = CreateCompatibleDC(dc); + HDC xorDc = CreateCompatibleDC(dc); + HBITMAP andBitmap = CreateCompatibleBitmap(dc, sourceRect.getWidth(), sourceRect.getHeight()); + HBITMAP xorBitmap = CreateCompatibleBitmap(dc, sourceRect.getWidth(), sourceRect.getHeight()); - HBITMAP oldAndBitmap = (HBITMAP)SelectObject(andDc, andBitmap); - HBITMAP oldXorBitmap = (HBITMAP)SelectObject(xorDc, xorBitmap); + HBITMAP oldAndBitmap = (HBITMAP)SelectObject(andDc, andBitmap); + HBITMAP oldXorBitmap = (HBITMAP)SelectObject(xorDc, xorBitmap); - video::ECOLOR_FORMAT format = tex->getColorFormat(); - u32 bytesPerPixel = video::IImage::getBitsPerPixelFromFormat(format) / 8; - u32 bytesLeftGap = sourceRect.UpperLeftCorner.X * bytesPerPixel; - u32 bytesRightGap = tex->getPitch() - sourceRect.LowerRightCorner.X * bytesPerPixel; - const u8* data = (const u8*)tex->lock(video::ETLM_READ_ONLY, 0); - data += sourceRect.UpperLeftCorner.Y*tex->getPitch(); - for ( s32 y = 0; y < sourceRect.getHeight(); ++y ) - { - data += bytesLeftGap; - for ( s32 x = 0; x < sourceRect.getWidth(); ++x ) - { - video::SColor pixelCol; - pixelCol.setData((const void*)data, format); - data += bytesPerPixel; + video::ECOLOR_FORMAT format = tex->getColorFormat(); + u32 bytesPerPixel = video::IImage::getBitsPerPixelFromFormat(format) / 8; + u32 bytesLeftGap = sourceRect.UpperLeftCorner.X * bytesPerPixel; + u32 bytesRightGap = tex->getPitch() - sourceRect.LowerRightCorner.X * bytesPerPixel; + const u8* data = (const u8*)tex->lock(video::ETLM_READ_ONLY, 0); + data += sourceRect.UpperLeftCorner.Y*tex->getPitch(); + for ( s32 y = 0; y < sourceRect.getHeight(); ++y ) + { + data += bytesLeftGap; + for ( s32 x = 0; x < sourceRect.getWidth(); ++x ) + { + video::SColor pixelCol; + pixelCol.setData((const void*)data, format); + data += bytesPerPixel; - if ( pixelCol.getAlpha() == 0 ) // transparent - { - SetPixel(andDc, x, y, RGB(255,255,255)); - SetPixel(xorDc, x, y, RGB(0,0,0)); - } - else // color - { - SetPixel(andDc, x, y, RGB(0,0,0)); - SetPixel(xorDc, x, y, RGB(pixelCol.getRed(), pixelCol.getGreen(), pixelCol.getBlue())); - } - } - data += bytesRightGap; - } - tex->unlock(); + if ( pixelCol.getAlpha() == 0 ) // transparent + { + SetPixel(andDc, x, y, RGB(255,255,255)); + SetPixel(xorDc, x, y, RGB(0,0,0)); + } + else // color + { + SetPixel(andDc, x, y, RGB(0,0,0)); + SetPixel(xorDc, x, y, RGB(pixelCol.getRed(), pixelCol.getGreen(), pixelCol.getBlue())); + } + } + data += bytesRightGap; + } + tex->unlock(); - SelectObject(andDc, oldAndBitmap); - SelectObject(xorDc, oldXorBitmap); + SelectObject(andDc, oldAndBitmap); + SelectObject(xorDc, oldXorBitmap); - DeleteDC(xorDc); - DeleteDC(andDc); + DeleteDC(xorDc); + DeleteDC(andDc); - ReleaseDC(hwnd, dc); + ReleaseDC(hwnd, dc); - // create the cursor + // create the cursor - ICONINFO iconinfo; - iconinfo.fIcon = false; // type is cursor not icon - iconinfo.xHotspot = hotspot.X; - iconinfo.yHotspot = hotspot.Y; - iconinfo.hbmMask = andBitmap; - iconinfo.hbmColor = xorBitmap; + ICONINFO iconinfo; + iconinfo.fIcon = false; // type is cursor not icon + iconinfo.xHotspot = hotspot.X; + iconinfo.yHotspot = hotspot.Y; + iconinfo.hbmMask = andBitmap; + iconinfo.hbmColor = xorBitmap; - HCURSOR cursor = CreateIconIndirect(&iconinfo); + HCURSOR cursor = CreateIconIndirect(&iconinfo); - DeleteObject(andBitmap); - DeleteObject(xorBitmap); + DeleteObject(andBitmap); + DeleteObject(xorBitmap); - return cursor; + return cursor; } CIrrDeviceWin32::CCursorControl::CCursorControl(CIrrDeviceWin32* device, const core::dimension2d& wsize, HWND hwnd, bool fullscreen) - : Device(device), WindowSize(wsize), InvWindowSize(0.0f, 0.0f), - HWnd(hwnd), BorderX(0), BorderY(0), - UseReferenceRect(false), IsVisible(true) - , ActiveIcon(gui::ECI_NORMAL), ActiveIconStartTime(0) + : Device(device), WindowSize(wsize), InvWindowSize(0.0f, 0.0f), + HWnd(hwnd), BorderX(0), BorderY(0), + UseReferenceRect(false), IsVisible(true) + , ActiveIcon(gui::ECI_NORMAL), ActiveIconStartTime(0) { - if (WindowSize.Width!=0) - InvWindowSize.Width = 1.0f / WindowSize.Width; + if (WindowSize.Width!=0) + InvWindowSize.Width = 1.0f / WindowSize.Width; - if (WindowSize.Height!=0) - InvWindowSize.Height = 1.0f / WindowSize.Height; + if (WindowSize.Height!=0) + InvWindowSize.Height = 1.0f / WindowSize.Height; - updateBorderSize(fullscreen, false); - initCursors(); + updateBorderSize(fullscreen, false); + initCursors(); } CIrrDeviceWin32::CCursorControl::~CCursorControl() { - for ( u32 i=0; i < Cursors.size(); ++i ) - { - for ( u32 f=0; f < Cursors[i].Frames.size(); ++f ) - { - DestroyCursor(Cursors[i].Frames[f].IconHW); - } - } + for ( u32 i=0; i < Cursors.size(); ++i ) + { + for ( u32 f=0; f < Cursors[i].Frames.size(); ++f ) + { + DestroyCursor(Cursors[i].Frames[f].IconHW); + } + } } void CIrrDeviceWin32::CCursorControl::initCursors() { - Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_ARROW)) ); - Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_CROSS)) ); - Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_HAND)) ); - Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_HELP)) ); - Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_IBEAM)) ); - Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_NO)) ); - Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_WAIT)) ); - Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_SIZEALL)) ); - Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_SIZENESW)) ); - Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_SIZENWSE)) ); - Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_SIZENS)) ); - Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_SIZEWE)) ); - Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_UPARROW)) ); + Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_ARROW)) ); + Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_CROSS)) ); + Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_HAND)) ); + Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_HELP)) ); + Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_IBEAM)) ); + Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_NO)) ); + Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_WAIT)) ); + Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_SIZEALL)) ); + Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_SIZENESW)) ); + Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_SIZENWSE)) ); + Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_SIZENS)) ); + Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_SIZEWE)) ); + Cursors.push_back( CursorW32(LoadCursor(NULL, IDC_UPARROW)) ); } void CIrrDeviceWin32::CCursorControl::update() { - if ( !Cursors[ActiveIcon].Frames.empty() && Cursors[ActiveIcon].FrameTime ) - { - // update animated cursors. This could also be done by X11 in case someone wants to figure that out (this way was just easier to implement) - u32 now = Device->getTimer()->getRealTime(); - u32 frame = ((now - ActiveIconStartTime) / Cursors[ActiveIcon].FrameTime) % Cursors[ActiveIcon].Frames.size(); - SetCursor( Cursors[ActiveIcon].Frames[frame].IconHW ); - } + if ( !Cursors[ActiveIcon].Frames.empty() && Cursors[ActiveIcon].FrameTime ) + { + // update animated cursors. This could also be done by X11 in case someone wants to figure that out (this way was just easier to implement) + u32 now = Device->getTimer()->getRealTime(); + u32 frame = ((now - ActiveIconStartTime) / Cursors[ActiveIcon].FrameTime) % Cursors[ActiveIcon].Frames.size(); + SetCursor( Cursors[ActiveIcon].Frames[frame].IconHW ); + } } //! Sets the active cursor icon void CIrrDeviceWin32::CCursorControl::setActiveIcon(gui::ECURSOR_ICON iconId) { - if ( iconId >= (s32)Cursors.size() ) - return; + if ( iconId >= (s32)Cursors.size() ) + return; - ActiveIcon = iconId; - ActiveIconStartTime = Device->getTimer()->getRealTime(); - if ( Cursors[ActiveIcon].Frames.size() ) - SetCursor( Cursors[ActiveIcon].Frames[0].IconHW ); + ActiveIcon = iconId; + ActiveIconStartTime = Device->getTimer()->getRealTime(); + if ( Cursors[ActiveIcon].Frames.size() ) + SetCursor( Cursors[ActiveIcon].Frames[0].IconHW ); } //! Add a custom sprite as cursor icon. gui::ECURSOR_ICON CIrrDeviceWin32::CCursorControl::addIcon(const gui::SCursorSprite& icon) { - if ( icon.SpriteId >= 0 ) - { - CursorW32 cW32; - cW32.FrameTime = icon.SpriteBank->getSprites()[icon.SpriteId].frameTime; + if ( icon.SpriteId >= 0 ) + { + CursorW32 cW32; + cW32.FrameTime = icon.SpriteBank->getSprites()[icon.SpriteId].frameTime; - for ( u32 i=0; i < icon.SpriteBank->getSprites()[icon.SpriteId].Frames.size(); ++i ) - { - irr::u32 texId = icon.SpriteBank->getSprites()[icon.SpriteId].Frames[i].textureNumber; - irr::u32 rectId = icon.SpriteBank->getSprites()[icon.SpriteId].Frames[i].rectNumber; - irr::core::rect rectIcon = icon.SpriteBank->getPositions()[rectId]; + for ( u32 i=0; i < icon.SpriteBank->getSprites()[icon.SpriteId].Frames.size(); ++i ) + { + irr::u32 texId = icon.SpriteBank->getSprites()[icon.SpriteId].Frames[i].textureNumber; + irr::u32 rectId = icon.SpriteBank->getSprites()[icon.SpriteId].Frames[i].rectNumber; + irr::core::rect rectIcon = icon.SpriteBank->getPositions()[rectId]; - HCURSOR hc = Device->TextureToCursor(HWnd, icon.SpriteBank->getTexture(texId), rectIcon, icon.HotSpot); - cW32.Frames.push_back( CursorFrameW32(hc) ); - } + HCURSOR hc = Device->TextureToCursor(HWnd, icon.SpriteBank->getTexture(texId), rectIcon, icon.HotSpot); + cW32.Frames.push_back( CursorFrameW32(hc) ); + } - Cursors.push_back( cW32 ); - return (gui::ECURSOR_ICON)(Cursors.size() - 1); - } - return gui::ECI_NORMAL; + Cursors.push_back( cW32 ); + return (gui::ECURSOR_ICON)(Cursors.size() - 1); + } + return gui::ECI_NORMAL; } //! replace the given cursor icon. void CIrrDeviceWin32::CCursorControl::changeIcon(gui::ECURSOR_ICON iconId, const gui::SCursorSprite& icon) { - if ( iconId >= (s32)Cursors.size() ) - return; + if ( iconId >= (s32)Cursors.size() ) + return; - for ( u32 i=0; i < Cursors[iconId].Frames.size(); ++i ) - DestroyCursor(Cursors[iconId].Frames[i].IconHW); + for ( u32 i=0; i < Cursors[iconId].Frames.size(); ++i ) + DestroyCursor(Cursors[iconId].Frames[i].IconHW); - if ( icon.SpriteId >= 0 ) - { - CursorW32 cW32; - cW32.FrameTime = icon.SpriteBank->getSprites()[icon.SpriteId].frameTime; - for ( u32 i=0; i < icon.SpriteBank->getSprites()[icon.SpriteId].Frames.size(); ++i ) - { - irr::u32 texId = icon.SpriteBank->getSprites()[icon.SpriteId].Frames[i].textureNumber; - irr::u32 rectId = icon.SpriteBank->getSprites()[icon.SpriteId].Frames[i].rectNumber; - irr::core::rect rectIcon = icon.SpriteBank->getPositions()[rectId]; + if ( icon.SpriteId >= 0 ) + { + CursorW32 cW32; + cW32.FrameTime = icon.SpriteBank->getSprites()[icon.SpriteId].frameTime; + for ( u32 i=0; i < icon.SpriteBank->getSprites()[icon.SpriteId].Frames.size(); ++i ) + { + irr::u32 texId = icon.SpriteBank->getSprites()[icon.SpriteId].Frames[i].textureNumber; + irr::u32 rectId = icon.SpriteBank->getSprites()[icon.SpriteId].Frames[i].rectNumber; + irr::core::rect rectIcon = icon.SpriteBank->getPositions()[rectId]; - HCURSOR hc = Device->TextureToCursor(HWnd, icon.SpriteBank->getTexture(texId), rectIcon, icon.HotSpot); - cW32.Frames.push_back( CursorFrameW32(hc) ); - } + HCURSOR hc = Device->TextureToCursor(HWnd, icon.SpriteBank->getTexture(texId), rectIcon, icon.HotSpot); + cW32.Frames.push_back( CursorFrameW32(hc) ); + } - Cursors[iconId] = cW32; - } + Cursors[iconId] = cW32; + } } //! Return a system-specific size which is supported for cursors. Larger icons will fail, smaller icons might work. core::dimension2di CIrrDeviceWin32::CCursorControl::getSupportedIconSize() const { - core::dimension2di result; + core::dimension2di result; - result.Width = GetSystemMetrics(SM_CXCURSOR); - result.Height = GetSystemMetrics(SM_CYCURSOR); + result.Width = GetSystemMetrics(SM_CXCURSOR); + result.Height = GetSystemMetrics(SM_CYCURSOR); - return result; + return result; } From cc6a1a39093a71ab7b3ca5dbbf2482123f2ffc53 Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 1 Aug 2017 17:48:18 +1000 Subject: [PATCH 009/125] Started to add XInput support (to be able to distinguish the two triggers). --- .../source/Irrlicht/CIrrDeviceWin32.cpp | 106 +++++++++++++----- 1 file changed, 78 insertions(+), 28 deletions(-) diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp index c17da02b2..168f6fb82 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp @@ -31,6 +31,7 @@ #ifdef _MSC_VER #pragma comment(lib, "winmm.lib") #endif +#include #endif #endif @@ -80,6 +81,8 @@ struct SJoystickWin32Control #else JOYCAPS Caps; #endif + /** For xbox-360 controller. */ + bool m_use_xinput; }; core::array ActiveJoysticks; #endif @@ -194,10 +197,11 @@ struct SJoystickWin32Control void pollJoysticks() { + if (0 == ActiveJoysticks.size()) + return; + #if defined _IRR_COMPILE_WITH_JOYSTICK_EVENTS_ #ifdef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ - if(0 == ActiveJoysticks.size()) - return; u32 joystick; DIJOYSTATE2 info; @@ -295,28 +299,45 @@ void pollJoysticks() } } #else - if (0 == ActiveJoysticks.size()) - return; - u32 joystick; - JOYINFOEX info; for(joystick = 0; joystick < ActiveJoysticks.size(); ++joystick) { - // needs to be reset for each joystick - // request ALL values and POV as continuous if possible - info.dwSize = sizeof(info); - info.dwFlags = JOY_RETURNALL|JOY_RETURNPOVCTS; - const JOYCAPS & caps = ActiveJoysticks[joystick].Caps; - // if no POV is available don't ask for POV values - if (!(caps.wCaps & JOYCAPS_HASPOV)) - info.dwFlags &= ~(JOY_RETURNPOV|JOY_RETURNPOVCTS); - if(JOYERR_NOERROR == joyGetPosEx(ActiveJoysticks[joystick].Index, &info)) - { - SEvent event; + SEvent event; + + event.EventType = irr::EET_JOYSTICK_INPUT_EVENT; + event.JoystickEvent.Joystick = (u8)joystick; + for (int axis = 0; axis < SEvent::SJoystickEvent::NUMBER_OF_AXES; ++axis) + event.JoystickEvent.Axis[axis] = 0; + + if (ActiveJoysticks[joystick].m_use_xinput) + { + XINPUT_STATE state; + memset(&state, 0, sizeof(state)); + DWORD result = XInputGetState(joystick, &state); + event.JoystickEvent.ButtonStates = state.Gamepad.wButtons; + // Thumb values are in [-32768, 32767] + event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_V] = state.Gamepad.sThumbRX; + event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_X] = state.Gamepad.sThumbRY; + event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Y] = state.Gamepad.sThumbLX; + event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Z] = state.Gamepad.sThumbLY; + event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_R] = state.Gamepad.bLeftTrigger*128; + event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_U] = state.Gamepad.bRightTrigger*128; + } + else // old API + { + JOYINFOEX info; + // needs to be reset for each joystick + // request ALL values and POV as continuous if possible + info.dwSize = sizeof(info); + info.dwFlags = JOY_RETURNALL | JOY_RETURNPOVCTS; + const JOYCAPS & caps = ActiveJoysticks[joystick].Caps; + // if no POV is available don't ask for POV values + if (!(caps.wCaps & JOYCAPS_HASPOV)) + info.dwFlags &= ~(JOY_RETURNPOV | JOY_RETURNPOVCTS); + if (joyGetPosEx(ActiveJoysticks[joystick].Index, &info) != JOYERR_NOERROR) + continue; - event.EventType = irr::EET_JOYSTICK_INPUT_EVENT; - event.JoystickEvent.Joystick = (u8)joystick; event.JoystickEvent.POV = (u16)info.dwPOV; // set to undefined if no POV value was returned or the value @@ -324,9 +345,6 @@ void pollJoysticks() if (!(info.dwFlags & JOY_RETURNPOV) || (event.JoystickEvent.POV > 35900)) event.JoystickEvent.POV = 65535; - for(int axis = 0; axis < SEvent::SJoystickEvent::NUMBER_OF_AXES; ++axis) - event.JoystickEvent.Axis[axis] = 0; - event.JoystickEvent.ButtonStates = info.dwButtons; switch(caps.wNumAxes) @@ -355,11 +373,10 @@ void pollJoysticks() case 1: event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_X] = (s16)((65535 * (info.dwXpos - caps.wXmin)) / (caps.wXmax - caps.wXmin) - 32768); - } - - (void)Device->postEventFromUser(event); - } - } + } // switch + } // if XInput ... else + (void)Device->postEventFromUser(event); + } // for all joysticks #endif #endif // _IRR_COMPILE_WITH_JOYSTICK_EVENTS_ } @@ -449,6 +466,7 @@ bool activateJoysticks(core::array & joystickInfo) joystickInfo.clear(); ActiveJoysticks.clear(); + // This number includes XInput and legacy devices const u32 numberOfJoysticks = ::joyGetNumDevs(); JOYINFOEX info; info.dwSize = sizeof(info); @@ -460,7 +478,34 @@ bool activateJoysticks(core::array & joystickInfo) joystickInfo.reallocate(numberOfJoysticks); ActiveJoysticks.reallocate(numberOfJoysticks); + // First discover all Xbox 360 controllers, which need to use + // XInput to get the state (otherwise the two triggers can not + // be used at the same time). u32 joystick = 0; + for (int i = 0; i < XUSER_MAX_COUNT; i++) + { + XINPUT_STATE state; + memset(&state, 0, sizeof(state)); + + DWORD result = XInputGetState(i, &state); + // Not connected or not an XInput device + if (result != ERROR_SUCCESS) continue; + + activeJoystick.Index = i; + activeJoystick.m_use_xinput = true; + ActiveJoysticks.push_back(activeJoystick); + + // This information is returned to the calling program + SJoystickInfo joy_info; + // 2 sticks with 2 directions each plus two triggers + joy_info.Axes = 6; + joy_info.Buttons = 30; + joy_info.Name = "XBOX 360 Gamepad"; + joy_info.HasGenericName = false; + joy_info.PovHat = SJoystickInfo::POV_HAT_PRESENT; + joystickInfo.push_back(joy_info); + } // for i < XUSER_MAX_COUNT + for(; joystick < numberOfJoysticks; ++joystick) { if(JOYERR_NOERROR == joyGetPosEx(joystick, &info) @@ -469,12 +514,17 @@ bool activateJoysticks(core::array & joystickInfo) &activeJoystick.Caps, sizeof(activeJoystick.Caps))) { + setJoystickName(joystick, activeJoystick.Caps, &returnInfo); + core::stringc low_name = returnInfo.Name.make_lower(); + // Ignore xbox controller, which are handled using XInput above + if (low_name.find("xbox") != -1) continue; + activeJoystick.Index = joystick; + activeJoystick.m_use_xinput = false; ActiveJoysticks.push_back(activeJoystick); returnInfo.Joystick = (u8)joystick; returnInfo.Axes = activeJoystick.Caps.wNumAxes; returnInfo.Buttons = activeJoystick.Caps.wNumButtons; - setJoystickName(joystick, activeJoystick.Caps, &returnInfo); returnInfo.PovHat = ((activeJoystick.Caps.wCaps & JOYCAPS_HASPOV) == JOYCAPS_HASPOV) ? SJoystickInfo::POV_HAT_PRESENT : SJoystickInfo::POV_HAT_ABSENT; From 1e5d2643beef2c737cc4d27c9add2b8644ba7dd4 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 2 Aug 2017 08:13:04 +1000 Subject: [PATCH 010/125] Bugfix: incorrect index. --- lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp index 168f6fb82..2138ab7c2 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp @@ -314,7 +314,7 @@ void pollJoysticks() { XINPUT_STATE state; memset(&state, 0, sizeof(state)); - DWORD result = XInputGetState(joystick, &state); + DWORD result = XInputGetState(ActiveJoysticks[joystick].Index, &state); event.JoystickEvent.ButtonStates = state.Gamepad.wButtons; // Thumb values are in [-32768, 32767] event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_V] = state.Gamepad.sThumbRX; From 22208365d22ef4b00fb74a871585b1151f653023 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 2 Aug 2017 08:13:24 +1000 Subject: [PATCH 011/125] Switch to direct input for joystick handling. --- lib/irrlicht/include/IrrCompileConfig.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/irrlicht/include/IrrCompileConfig.h b/lib/irrlicht/include/IrrCompileConfig.h index ccfd280e4..d262a9954 100644 --- a/lib/irrlicht/include/IrrCompileConfig.h +++ b/lib/irrlicht/include/IrrCompileConfig.h @@ -168,7 +168,7 @@ headers, e.g. Summer 2004. This is a Microsoft issue, not an Irrlicht one. //! Define _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ if you want to use DirectInput for joystick handling. /** This only applies to Windows devices, currently only supported under Win32 device. If not defined, Windows Multimedia library is used, which offers also broad support for joystick devices. */ -#undef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ +#define _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ #ifdef NO_IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ #undef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ #endif From f3a962c391db3e0dcddaea1d9f483c7e089250c5 Mon Sep 17 00:00:00 2001 From: Geoffrey Mon Date: Wed, 2 Aug 2017 12:49:14 -0400 Subject: [PATCH 012/125] Save and use usernames in replay files Fixes #2754. * Store usernames of each racer in recorded replay files * Display those usernames in a column of the replay selection UI and in race result dialogs * RaceResultGUI::getKartDisplayName functionality moved into Controller::getName --- src/karts/controller/controller.hpp | 10 ++++--- src/karts/controller/ghost_controller.cpp | 3 ++- src/karts/controller/ghost_controller.hpp | 6 ++++- src/replay/replay_base.hpp | 2 +- src/replay/replay_play.cpp | 18 +++++++++---- src/replay/replay_play.hpp | 24 ++++++++++------- src/replay/replay_recorder.cpp | 10 ++++--- src/states_screens/ghost_replay_selection.cpp | 6 +++++ src/states_screens/race_result_gui.cpp | 27 ++----------------- src/states_screens/race_result_gui.hpp | 1 - 10 files changed, 57 insertions(+), 50 deletions(-) diff --git a/src/karts/controller/controller.hpp b/src/karts/controller/controller.hpp index 747f9cc0c..98403badf 100644 --- a/src/karts/controller/controller.hpp +++ b/src/karts/controller/controller.hpp @@ -28,9 +28,9 @@ using namespace irr; */ #include "input/input.hpp" +#include "karts/abstract_kart.hpp" #include "states_screens/state_manager.hpp" -class AbstractKart; class Item; class KartControl; class Material; @@ -100,17 +100,19 @@ public: /** Only local players can get achievements. */ virtual bool canGetAchievements () const { return false; } // ------------------------------------------------------------------------ - /** This should only be called for End- and LocalPlayer-Controller. */ + /** Display name of the controller. + * Defaults to kart name; overriden by controller classes + * (such as player controllers) to display username. */ virtual core::stringw getName() const { - assert(false); - return core::stringw(""); + return translations->fribidize(m_kart->getName()); } // getName // ------------------------------------------------------------------------ /** Returns the kart controlled by this controller. */ AbstractKart *getKart() const { return m_kart; } }; // Controller +extern Translations* translations; #endif /* EOF */ diff --git a/src/karts/controller/ghost_controller.cpp b/src/karts/controller/ghost_controller.cpp index ce9fa8d96..99a4c8475 100644 --- a/src/karts/controller/ghost_controller.cpp +++ b/src/karts/controller/ghost_controller.cpp @@ -21,9 +21,10 @@ #include "karts/controller/kart_control.hpp" #include "modes/world.hpp" -GhostController::GhostController(AbstractKart *kart) +GhostController::GhostController(AbstractKart *kart, core::stringw display_name) : Controller(kart) { + m_display_name = display_name; } // GhostController //----------------------------------------------------------------------------- diff --git a/src/karts/controller/ghost_controller.hpp b/src/karts/controller/ghost_controller.hpp index c0eb462f9..c78b4b454 100644 --- a/src/karts/controller/ghost_controller.hpp +++ b/src/karts/controller/ghost_controller.hpp @@ -37,11 +37,14 @@ private: /** The current world time. */ float m_current_time; + /** Player name of the ghost kart. */ + core::stringw m_display_name; + /** The list of the times at which the events of kart were reached. */ std::vector m_all_times; public: - GhostController(AbstractKart *kart); + GhostController(AbstractKart *kart, core::stringw display_name); virtual ~GhostController() {}; virtual void reset() OVERRIDE; virtual void update (float dt) OVERRIDE; @@ -73,6 +76,7 @@ public: unsigned int getCurrentReplayIndex() const { return m_current_index; } // ------------------------------------------------------------------------ + core::stringw getName() const OVERRIDE { return m_display_name; } }; // GhostController #endif diff --git a/src/replay/replay_base.hpp b/src/replay/replay_base.hpp index 2d7f8b408..0655d2daf 100644 --- a/src/replay/replay_base.hpp +++ b/src/replay/replay_base.hpp @@ -81,7 +81,7 @@ protected: /** Returns the version number of the replay file. This is used to check * that a loaded replay file can still be understood by this * executable. */ - unsigned int getReplayVersion() const { return 3; } + unsigned int getReplayVersion() const { return 4; } public: ReplayBase(); diff --git a/src/replay/replay_play.cpp b/src/replay/replay_play.cpp index cc998ae74..942f07175 100644 --- a/src/replay/replay_play.cpp +++ b/src/replay/replay_play.cpp @@ -135,13 +135,20 @@ bool ReplayPlay::addReplayFile(const std::string& fn, bool custom_replay) is_end.trim(); if (is_end == "kart_list_end") break; char s1[1024]; + char display_name_encoded[1024]; - if (sscanf(s,"kart: %s", s1) != 1) + if (sscanf(s,"kart: %s %[^\n]", s1, display_name_encoded) != 2) { Log::warn("Replay", "Could not read ghost karts info!"); break; } rd.m_kart_list.push_back(std::string(s1)); + rd.m_name_list.push_back(StringUtils::xmlDecode(std::string(display_name_encoded))); + + if (rd.m_name_list.size() == 1) { + // First user is the game master and the "owner" of this replay file + rd.m_user_name = rd.m_name_list[0]; + } } int reverse = 0; @@ -249,11 +256,12 @@ void ReplayPlay::readKartData(FILE *fd, char *next_line) { char s[1024]; const unsigned int kart_num = m_ghost_karts.size(); - m_ghost_karts.push_back(new GhostKart(m_replay_file_list - [m_current_replay_file].m_kart_list.at(kart_num), - kart_num, kart_num + 1)); + ReplayData &rd = m_replay_file_list[m_current_replay_file]; + m_ghost_karts.push_back(new GhostKart(rd.m_kart_list.at(kart_num), + kart_num, kart_num + 1)); m_ghost_karts[kart_num].init(RaceManager::KT_GHOST); - Controller* controller = new GhostController(getGhostKart(kart_num)); + Controller* controller = new GhostController(getGhostKart(kart_num), + rd.m_name_list[kart_num]); getGhostKart(kart_num)->setController(controller); unsigned int size; diff --git a/src/replay/replay_play.hpp b/src/replay/replay_play.hpp index c589c7fc2..04b6ed7b4 100644 --- a/src/replay/replay_play.hpp +++ b/src/replay/replay_play.hpp @@ -45,20 +45,23 @@ public: SO_KART_NUM, SO_DIFF, SO_LAPS, - SO_TIME + SO_TIME, + SO_USER }; class ReplayData { public: - std::string m_filename; - std::string m_track_name; - std::vector m_kart_list; - bool m_reverse; - bool m_custom_replay_file; - unsigned int m_difficulty; - unsigned int m_laps; - float m_min_time; + std::string m_filename; + std::string m_track_name; + core::stringw m_user_name; + std::vector m_kart_list; + std::vector m_name_list; + bool m_reverse; + bool m_custom_replay_file; + unsigned int m_difficulty; + unsigned int m_laps; + float m_min_time; bool operator < (const ReplayData& r) const { @@ -82,6 +85,9 @@ public: case SO_TIME: return m_min_time < r.m_min_time; break; + case SO_USER: + return m_user_name < r.m_user_name; + break; } // switch return true; } // operator < diff --git a/src/replay/replay_recorder.cpp b/src/replay/replay_recorder.cpp index 4a1c986e2..e94a111e4 100644 --- a/src/replay/replay_recorder.cpp +++ b/src/replay/replay_recorder.cpp @@ -31,6 +31,7 @@ #include #include #include +#include ReplayRecorder *ReplayRecorder::m_replay_recorder = NULL; @@ -222,9 +223,12 @@ void ReplayRecorder::save() fprintf(fd, "version: %d\n", getReplayVersion()); for (unsigned int real_karts = 0; real_karts < num_karts; real_karts++) { - if (world->getKart(real_karts)->isGhostKart()) continue; - fprintf(fd, "kart: %s\n", - world->getKart(real_karts)->getIdent().c_str()); + const AbstractKart *kart = world->getKart(real_karts); + if (kart->isGhostKart()) continue; + + // XML encode the username to handle Unicode + fprintf(fd, "kart: %s %s\n", kart->getIdent().c_str(), + StringUtils::xmlEncode(kart->getController()->getName()).c_str()); } fprintf(fd, "kart_list_end\n"); diff --git a/src/states_screens/ghost_replay_selection.cpp b/src/states_screens/ghost_replay_selection.cpp index 7828efc4a..92fe70882 100644 --- a/src/states_screens/ghost_replay_selection.cpp +++ b/src/states_screens/ghost_replay_selection.cpp @@ -80,6 +80,7 @@ void GhostReplaySelection::beforeAddingWidget() m_replay_list_widget->addColumn( _("Difficulty"), 1); m_replay_list_widget->addColumn( _("Laps"), 1); m_replay_list_widget->addColumn( _("Finish Time"), 1); + m_replay_list_widget->addColumn( _("User"), 1); } // beforeAddingWidget // ---------------------------------------------------------------------------- @@ -122,6 +123,8 @@ void GhostReplaySelection::loadList() (StringUtils::toWString(rd.m_laps), -1, 1, true)); row.push_back(GUIEngine::ListWidget::ListCell (StringUtils::toWString(rd.m_min_time) + L"s", -1, 1, true)); + row.push_back(GUIEngine::ListWidget::ListCell + (rd.m_user_name, -1, 1, true)); m_replay_list_widget->addItem(StringUtils::toString(i), row); } } // loadList @@ -213,6 +216,9 @@ void GhostReplaySelection::onColumnClicked(int column_id) case 5: ReplayPlay::setSortOrder(ReplayPlay::SO_TIME); break; + case 6: + ReplayPlay::setSortOrder(ReplayPlay::SO_USER); + break; default: assert(0); break; diff --git a/src/states_screens/race_result_gui.cpp b/src/states_screens/race_result_gui.cpp index 7aee51f4f..0a1b65ec6 100644 --- a/src/states_screens/race_result_gui.cpp +++ b/src/states_screens/race_result_gui.cpp @@ -486,7 +486,7 @@ void RaceResultGUI::backToLobby() // Save a pointer to the current row_info entry RowInfo *ri = &(m_all_row_infos[position - first_position]); ri->m_is_player_kart = kart->getController()->isLocalPlayerController(); - ri->m_kart_name = getKartDisplayName(kart); + ri->m_kart_name = kart->getController()->getName(); video::ITexture *icon = kart->getKartProperties()->getIconMaterial()->getTexture(); @@ -855,7 +855,7 @@ void RaceResultGUI::backToLobby() ri->m_kart_icon = kart->getKartProperties()->getIconMaterial()->getTexture(); ri->m_is_player_kart = kart->getController()->isLocalPlayerController(); - ri->m_kart_name = getKartDisplayName(kart); + ri->m_kart_name = kart->getController()->getName(); // In FTL karts do have a time, which is shown even when the kart // is eliminated @@ -907,29 +907,6 @@ void RaceResultGUI::backToLobby() #endif } // determineGPLayout - //----------------------------------------------------------------------------- - /** Returns a string to display next to a kart. For a player that's the name - * of the player, for an AI kart it's the name of the driver. - */ - core::stringw RaceResultGUI::getKartDisplayName(const AbstractKart *kart) const - { - const EndController *ec = - dynamic_cast(kart->getController()); - // If the race was given up, there is no end controller for the - // players, so this case needs to be handled separately - if(ec && ec->isLocalPlayerController()) - return ec->getName(); - else - { - // No end controller, check explicitely for a player controller - const PlayerController *pc = - dynamic_cast(kart->getController()); - // Check if the kart is a player controller to get the real name - if(pc) return pc->getName(); - } - return translations->fribidize(kart->getName()); - } // getKartDisplayName - //----------------------------------------------------------------------------- /** Displays the race results for a single kart. * \param n Index of the kart to be displayed. diff --git a/src/states_screens/race_result_gui.hpp b/src/states_screens/race_result_gui.hpp index 75c663901..0c1960ca0 100644 --- a/src/states_screens/race_result_gui.hpp +++ b/src/states_screens/race_result_gui.hpp @@ -194,7 +194,6 @@ private: void displayPostRaceInfo(); void displaySoccerResults(); void displayScreenShots(); - irr::core::stringw getKartDisplayName(const AbstractKart *kart) const; int getFontHeight () const; From ca5258a46cfeac04704ccf7dfe50849fcc361d6b Mon Sep 17 00:00:00 2001 From: Geoffrey Mon Date: Wed, 2 Aug 2017 13:46:48 -0400 Subject: [PATCH 013/125] Move Controller::getName definition to avoid unnecessary #include --- src/karts/controller/controller.cpp | 4 ++++ src/karts/controller/controller.hpp | 7 ++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/karts/controller/controller.cpp b/src/karts/controller/controller.cpp index fe3450464..6460b0341 100644 --- a/src/karts/controller/controller.cpp +++ b/src/karts/controller/controller.cpp @@ -34,3 +34,7 @@ Controller::Controller(AbstractKart *kart) m_kart = kart; setControllerName("Controller"); } // Controller + +core::stringw Controller::getName() const { + return translations->fribidize(m_kart->getName()); +} diff --git a/src/karts/controller/controller.hpp b/src/karts/controller/controller.hpp index 98403badf..1e101d137 100644 --- a/src/karts/controller/controller.hpp +++ b/src/karts/controller/controller.hpp @@ -28,9 +28,9 @@ using namespace irr; */ #include "input/input.hpp" -#include "karts/abstract_kart.hpp" #include "states_screens/state_manager.hpp" +class AbstractKart; class Item; class KartControl; class Material; @@ -103,10 +103,7 @@ public: /** Display name of the controller. * Defaults to kart name; overriden by controller classes * (such as player controllers) to display username. */ - virtual core::stringw getName() const - { - return translations->fribidize(m_kart->getName()); - } // getName + virtual core::stringw getName() const; // ------------------------------------------------------------------------ /** Returns the kart controlled by this controller. */ AbstractKart *getKart() const { return m_kart; } From 9ec2ff5c4a0b8223a59f7b649581962a2026f81f Mon Sep 17 00:00:00 2001 From: Geoffrey Mon Date: Wed, 2 Aug 2017 14:13:26 -0400 Subject: [PATCH 014/125] Backwards compatibility: use kart name if username is not in replay --- src/karts/controller/ghost_controller.hpp | 5 ++++- src/replay/replay_base.hpp | 2 +- src/replay/replay_play.cpp | 23 ++++++++++++++++------- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/karts/controller/ghost_controller.hpp b/src/karts/controller/ghost_controller.hpp index c78b4b454..746d95544 100644 --- a/src/karts/controller/ghost_controller.hpp +++ b/src/karts/controller/ghost_controller.hpp @@ -76,7 +76,10 @@ public: unsigned int getCurrentReplayIndex() const { return m_current_index; } // ------------------------------------------------------------------------ - core::stringw getName() const OVERRIDE { return m_display_name; } + /** Return the display name; if not set, use default display name (kart name) */ + core::stringw getName() const OVERRIDE { return m_display_name.empty() ? + Controller::getName() : + m_display_name; } }; // GhostController #endif diff --git a/src/replay/replay_base.hpp b/src/replay/replay_base.hpp index 0655d2daf..2d7f8b408 100644 --- a/src/replay/replay_base.hpp +++ b/src/replay/replay_base.hpp @@ -81,7 +81,7 @@ protected: /** Returns the version number of the replay file. This is used to check * that a loaded replay file can still be understood by this * executable. */ - unsigned int getReplayVersion() const { return 4; } + unsigned int getReplayVersion() const { return 3; } public: ReplayBase(); diff --git a/src/replay/replay_play.cpp b/src/replay/replay_play.cpp index 942f07175..130dbad0a 100644 --- a/src/replay/replay_play.cpp +++ b/src/replay/replay_play.cpp @@ -137,17 +137,26 @@ bool ReplayPlay::addReplayFile(const std::string& fn, bool custom_replay) char s1[1024]; char display_name_encoded[1024]; - if (sscanf(s,"kart: %s %[^\n]", s1, display_name_encoded) != 2) + int scanned = sscanf(s,"kart: %s %[^\n]", s1, display_name_encoded); + if (scanned < 1) { Log::warn("Replay", "Could not read ghost karts info!"); break; - } - rd.m_kart_list.push_back(std::string(s1)); - rd.m_name_list.push_back(StringUtils::xmlDecode(std::string(display_name_encoded))); + } else { + rd.m_kart_list.push_back(std::string(s1)); - if (rd.m_name_list.size() == 1) { - // First user is the game master and the "owner" of this replay file - rd.m_user_name = rd.m_name_list[0]; + if (scanned == 2) { + // If username of kart is present, use it + rd.m_name_list.push_back(StringUtils::xmlDecode(std::string(display_name_encoded))); + if (rd.m_name_list.size() == 1) { + // First user is the game master and the "owner" of this replay file + rd.m_user_name = rd.m_name_list[0]; + } + } else { // scanned == 1 + // If username is not present, kart display name will default to kart name + // (see GhostController::getName) + rd.m_name_list.push_back(""); + } } } From 802d70ca8d7f50adc0d39e28ef56984cd1b8e2c8 Mon Sep 17 00:00:00 2001 From: Geoffrey Mon Date: Wed, 2 Aug 2017 17:21:37 -0400 Subject: [PATCH 015/125] Fix code style issues --- src/karts/controller/controller.cpp | 3 ++- src/karts/controller/ghost_controller.hpp | 7 +++--- src/replay/replay_play.cpp | 28 ++++++++++++----------- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/karts/controller/controller.cpp b/src/karts/controller/controller.cpp index 6460b0341..26d8311d2 100644 --- a/src/karts/controller/controller.cpp +++ b/src/karts/controller/controller.cpp @@ -35,6 +35,7 @@ Controller::Controller(AbstractKart *kart) setControllerName("Controller"); } // Controller -core::stringw Controller::getName() const { +core::stringw Controller::getName() const +{ return translations->fribidize(m_kart->getName()); } diff --git a/src/karts/controller/ghost_controller.hpp b/src/karts/controller/ghost_controller.hpp index 746d95544..245abae74 100644 --- a/src/karts/controller/ghost_controller.hpp +++ b/src/karts/controller/ghost_controller.hpp @@ -77,9 +77,10 @@ public: { return m_current_index; } // ------------------------------------------------------------------------ /** Return the display name; if not set, use default display name (kart name) */ - core::stringw getName() const OVERRIDE { return m_display_name.empty() ? - Controller::getName() : - m_display_name; } + core::stringw getName() const OVERRIDE + { + return m_display_name.empty() ? Controller::getName() : m_display_name; + } }; // GhostController #endif diff --git a/src/replay/replay_play.cpp b/src/replay/replay_play.cpp index 130dbad0a..923374af9 100644 --- a/src/replay/replay_play.cpp +++ b/src/replay/replay_play.cpp @@ -142,21 +142,23 @@ bool ReplayPlay::addReplayFile(const std::string& fn, bool custom_replay) { Log::warn("Replay", "Could not read ghost karts info!"); break; - } else { - rd.m_kart_list.push_back(std::string(s1)); + } - if (scanned == 2) { - // If username of kart is present, use it - rd.m_name_list.push_back(StringUtils::xmlDecode(std::string(display_name_encoded))); - if (rd.m_name_list.size() == 1) { - // First user is the game master and the "owner" of this replay file - rd.m_user_name = rd.m_name_list[0]; - } - } else { // scanned == 1 - // If username is not present, kart display name will default to kart name - // (see GhostController::getName) - rd.m_name_list.push_back(""); + rd.m_kart_list.push_back(std::string(s1)); + if (scanned == 2) + { + // If username of kart is present, use it + rd.m_name_list.push_back(StringUtils::xmlDecode(std::string(display_name_encoded))); + if (rd.m_name_list.size() == 1) + { + // First user is the game master and the "owner" of this replay file + rd.m_user_name = rd.m_name_list[0]; } + } else + { // scanned == 1 + // If username is not present, kart display name will default to kart name + // (see GhostController::getName) + rd.m_name_list.push_back(""); } } From d3da93f3682bc8f893616e29d952d50830c04d1b Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 3 Aug 2017 09:24:50 +1000 Subject: [PATCH 016/125] Try to use a Microsoft function to detect XInput devices when using DirectInput - atm not working yet :( --- .../source/Irrlicht/CIrrDeviceWin32.cpp | 139 +++++++++++++++++- 1 file changed, 138 insertions(+), 1 deletion(-) diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp index 2138ab7c2..619a359be 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp @@ -23,6 +23,9 @@ #ifdef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ #define DIRECTINPUT_VERSION 0x0800 #include +#include +#include + #ifdef _MSC_VER #pragma comment(lib, "dinput8.lib") #pragma comment(lib, "dxguid.lib") @@ -123,12 +126,15 @@ struct SJoystickWin32Control p->directInputAddJoystick(lpddi); return DIENUM_CONTINUE; } + bool IsXInputDevice(const GUID* pGuidProductFromDirectInput); + void directInputAddJoystick(LPCDIDEVICEINSTANCE lpddi) { //Get the GUID of the joystuck const GUID guid = lpddi->guidInstance; - JoystickInfo activeJoystick; + activeJoystick.m_use_xinput = isXInputDevice(&guid); + activeJoystick.Index=ActiveJoysticks.size(); activeJoystick.guid=guid; activeJoystick.Name=lpddi->tszProductName; @@ -193,6 +199,137 @@ struct SJoystickWin32Control ActiveJoysticks.push_back(activeJoystick); } + + //----------------------------------------------------------------------------- + /** Taken from: + * https://msdn.microsoft.com/en-us/library/windows/desktop/dd940435(v=vs.85).aspx + */ + template void SAFE_RELEASE(T **ppT) + { + if (*ppT) + { + (*ppT)->Release(); + *ppT = NULL; + } + } + + /** Taken from: + * https://msdn.microsoft.com/en-us/library/windows/desktop/ee417014(v=vs.85).aspx + */ + //----------------------------------------------------------------------------- + // Enum each PNP device using WMI and check each device ID to see if it contains + // "IG_" (ex. "VID_045E&PID_028E&IG_00"). If it does, then it's an XInput device + // Unfortunately this information can not be found by just using DirectInput + //----------------------------------------------------------------------------- + bool isXInputDevice(const GUID* pGuidProductFromDirectInput) + { + IWbemLocator* pIWbemLocator = NULL; + IEnumWbemClassObject* pEnumDevices = NULL; + IWbemClassObject* pDevices[20] = { 0 }; + IWbemServices* pIWbemServices = NULL; + BSTR bstrNamespace = NULL; + BSTR bstrDeviceID = NULL; + BSTR bstrClassName = NULL; + DWORD uReturned = 0; + bool bIsXinputDevice = false; + UINT iDevice = 0; + VARIANT var; + HRESULT hr; + + // CoInit if needed + hr = CoInitialize(NULL); + bool bCleanupCOM = SUCCEEDED(hr); + + // Create WMI + hr = CoCreateInstance(__uuidof(WbemLocator), + NULL, + CLSCTX_INPROC_SERVER, + __uuidof(IWbemLocator), + (LPVOID*)&pIWbemLocator); + if (FAILED(hr) || pIWbemLocator == NULL) + goto LCleanup; + + bstrNamespace = SysAllocString(L"\\\\.\\root\\cimv2"); if (bstrNamespace == NULL) goto LCleanup; + bstrClassName = SysAllocString(L"Win32_PNPEntity"); if (bstrClassName == NULL) goto LCleanup; + bstrDeviceID = SysAllocString(L"DeviceID"); if (bstrDeviceID == NULL) goto LCleanup; + + // Connect to WMI + hr = pIWbemLocator->ConnectServer(bstrNamespace, NULL, NULL, 0L, + 0L, NULL, NULL, &pIWbemServices); + if (FAILED(hr) || pIWbemServices == NULL) + goto LCleanup; + + // Switch security level to IMPERSONATE. + CoSetProxyBlanket(pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, + RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE); + + hr = pIWbemServices->CreateInstanceEnum(bstrClassName, 0, NULL, &pEnumDevices); + if (FAILED(hr) || pEnumDevices == NULL) + goto LCleanup; + + // Loop over all devices + for (;; ) + { + // Get 20 at a time + hr = pEnumDevices->Next(10000, 20, pDevices, &uReturned); + if (FAILED(hr)) + goto LCleanup; + if (uReturned == 0) + break; + + for (iDevice = 0; iDeviceGet(bstrDeviceID, 0L, &var, NULL, NULL); + if (SUCCEEDED(hr) && var.vt == VT_BSTR && var.bstrVal != NULL) + { + // Check if the device ID contains "IG_". If it does, then it's an XInput device + // This information can not be found from DirectInput + if (wcsstr(var.bstrVal, L"IG_")) + { + // If it does, then get the VID/PID from var.bstrVal + DWORD dwPid = 0, dwVid = 0; + WCHAR* strVid = wcsstr(var.bstrVal, L"VID_"); + if (strVid && swscanf(strVid, L"VID_%4X", &dwVid) != 1) + dwVid = 0; + WCHAR* strPid = wcsstr(var.bstrVal, L"PID_"); + if (strPid && swscanf(strPid, L"PID_%4X", &dwPid) != 1) + dwPid = 0; + + // Compare the VID/PID to the DInput device + DWORD dwVidPid = MAKELONG(dwVid, dwPid); + if (dwVidPid == pGuidProductFromDirectInput->Data1) + { + bIsXinputDevice = true; + goto LCleanup; + } + } + } + SAFE_RELEASE(&pDevices[iDevice]); + } + } + + LCleanup: + if (bstrNamespace) + SysFreeString(bstrNamespace); + if (bstrDeviceID) + SysFreeString(bstrDeviceID); + if (bstrClassName) + SysFreeString(bstrClassName); + for (iDevice = 0; iDevice<20; iDevice++) + SAFE_RELEASE(&pDevices[iDevice]); + SAFE_RELEASE(&pEnumDevices); + SAFE_RELEASE(&pIWbemLocator); + SAFE_RELEASE(&pIWbemServices); + + if (bCleanupCOM) + CoUninitialize(); + + return bIsXinputDevice; + } + + // ------------------------------------------------------------------------ + #endif void pollJoysticks() From f4317302784a5a15bff485cd3ffb5973b96d485a Mon Sep 17 00:00:00 2001 From: hiker Date: Sat, 5 Aug 2017 00:12:22 +1000 Subject: [PATCH 017/125] Fixed recognition of XInput devices. --- lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp index 619a359be..73ff7ca55 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp @@ -131,7 +131,7 @@ struct SJoystickWin32Control void directInputAddJoystick(LPCDIDEVICEINSTANCE lpddi) { //Get the GUID of the joystuck - const GUID guid = lpddi->guidInstance; + const GUID guid = lpddi->guidProduct; JoystickInfo activeJoystick; activeJoystick.m_use_xinput = isXInputDevice(&guid); From 68d41a07ba2caae04db766d189030242ea079fee Mon Sep 17 00:00:00 2001 From: hiker Date: Sat, 5 Aug 2017 00:34:51 +1000 Subject: [PATCH 018/125] Now actually use XInput to read the joystick events for all XInput devices. --- .../source/Irrlicht/CIrrDeviceWin32.cpp | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp index 73ff7ca55..6a34a3cec 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp @@ -20,6 +20,7 @@ #include #include #if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) +#include #ifdef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ #define DIRECTINPUT_VERSION 0x0800 #include @@ -34,7 +35,6 @@ #ifdef _MSC_VER #pragma comment(lib, "winmm.lib") #endif -#include #endif #endif @@ -345,6 +345,31 @@ void pollJoysticks() for(joystick = 0; joystick < ActiveJoysticks.size(); ++joystick) { + SEvent event; + + event.EventType = irr::EET_JOYSTICK_INPUT_EVENT; + event.JoystickEvent.Joystick = (u8)joystick; + + if (ActiveJoysticks[joystick].m_use_xinput) + { + XINPUT_STATE state; + memset(&state, 0, sizeof(state)); + DWORD result = XInputGetState(ActiveJoysticks[joystick].Index, &state); + event.JoystickEvent.ButtonStates = state.Gamepad.wButtons; + // Map the axis as they were previously, so existing configs + // still work as expected. The Y axis needs to be reversed: + // -32768 --> 32767, ..., 32767 --> -32768 + // Inverting the bits with ~ does that (-sThumbLY would map -32768 to -32768!!) + event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_X] = state.Gamepad.sThumbLX; + event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Y] = ~state.Gamepad.sThumbLY; + event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Z] = state.Gamepad.bLeftTrigger * 128; + event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_R] = state.Gamepad.sThumbRX; + event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_U] = ~state.Gamepad.sThumbRY; + event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_V] = state.Gamepad.bRightTrigger * 128; + (void)Device->postEventFromUser(event); + + continue; + } // needs to be reset for each joystick // request ALL values and POV as continuous if possible @@ -353,11 +378,6 @@ void pollJoysticks() if (!FAILED(ActiveJoysticks[joystick].lpdijoy->GetDeviceState(sizeof(info),&info))) { - SEvent event; - - event.EventType = irr::EET_JOYSTICK_INPUT_EVENT; - event.JoystickEvent.Joystick = (u8)joystick; - event.JoystickEvent.POV = (u16)info.rgdwPOV[0]; // set to undefined if no POV value was returned or the value // is out of range From 891e4392645d47ca7827090d8adc81e3ff6338f6 Mon Sep 17 00:00:00 2001 From: hiker Date: Sun, 6 Aug 2017 22:56:59 +1000 Subject: [PATCH 019/125] Remapped XInput to be the same as DirectInput buttons, so that existing configs works as expected. Hard-coded 6 axis for xbox controller to allow for triggers to be reported as two different axis (instead of as one axis). Updated gamepad config to use proper names for triggers. --- .../source/Irrlicht/CIrrDeviceWin32.cpp | 38 ++++++++++++++++++- src/input/gamepad_config.cpp | 8 ++-- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp index 6a34a3cec..04d568a36 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp @@ -197,6 +197,11 @@ struct SJoystickWin32Control activeJoystick.axisValid[i+caxis]=1; } + // On Xbox 360 devices left and right trigger are reported as two + // different axes (instead of 1 with DirectInput), so we need one + // additional axis: + if (activeJoystick.m_use_xinput && activeJoystick.devcaps.dwAxes == 5) + activeJoystick.devcaps.dwAxes = 6; ActiveJoysticks.push_back(activeJoystick); } @@ -355,8 +360,37 @@ void pollJoysticks() XINPUT_STATE state; memset(&state, 0, sizeof(state)); DWORD result = XInputGetState(ActiveJoysticks[joystick].Index, &state); - event.JoystickEvent.ButtonStates = state.Gamepad.wButtons; - // Map the axis as they were previously, so existing configs + // XInput reports the buttons in a different order. So to keep + // old configs to work as expected, remap the buttons. + int abxy = (state.Gamepad.wButtons & (XINPUT_GAMEPAD_A | + XINPUT_GAMEPAD_B | + XINPUT_GAMEPAD_X | + XINPUT_GAMEPAD_Y ) + ) >> 12; + int shoulder = (state.Gamepad.wButtons & (XINPUT_GAMEPAD_LEFT_SHOULDER| + XINPUT_GAMEPAD_RIGHT_SHOULDER) + ) >> 4; + int start = (state.Gamepad.wButtons & XINPUT_GAMEPAD_START) << 3; + int back = (state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK ) << 1; + + event.JoystickEvent.ButtonStates = abxy | shoulder | start | back; + int angle = 65535; + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) + { + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) angle = 22500; + else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) angle = 13500; + else angle = 18000; + } + else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) + { + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) angle = 31500; + else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) angle = 4500; + else angle = 0; + } + else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) angle = 9000; + else if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) angle = 27000; + event.JoystickEvent.POV = angle; + // Map the axis as they were previously, so existing configs // still work as expected. The Y axis needs to be reversed: // -32768 --> 32767, ..., 32767 --> -32768 // Inverting the bits with ~ does that (-sThumbLY would map -32768 to -32768!!) diff --git a/src/input/gamepad_config.cpp b/src/input/gamepad_config.cpp index 0be20e5e8..9670eb205 100644 --- a/src/input/gamepad_config.cpp +++ b/src/input/gamepad_config.cpp @@ -236,10 +236,7 @@ core::stringw GamepadConfig::getBindingAsString(const PlayerAction action) const // I18N: name of stick on gamepads : _("Left thumb up"); // I18N: name of stick on gamepads - case 2: return (ad==Input::AD_POSITIVE) ? _("Left trigger") - // I18N: name of stick on gamepads - : _("Right trigger"); - // I18N: name of stick on gamepads + case 2: return _("Left trigger"); // I18N: name of trigger on gamepads case 3: return (ad==Input::AD_POSITIVE) ? _("Right thumb down") // I18N: name of stick on gamepads : _("Right thumb up"); @@ -248,7 +245,8 @@ core::stringw GamepadConfig::getBindingAsString(const PlayerAction action) const // I18N: name of stick on gamepads : _("Right thumb left"); // I18N: name of buttons on gamepads - case Input::HAT_H_ID: return (ad == Input::AD_POSITIVE) ? _("DPad up") + case 5: return _("Right trigger"); // I18N: name of trigger on gamepads + case Input::HAT_H_ID: return (ad == Input::AD_POSITIVE) ? _("DPad up") // I18N: name of buttons on gamepads : _("DPad down"); // I18N: name of buttons on gamepads From e6c2c3df95af542eae44c40a0db0168abfbfaa8c Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 7 Aug 2017 09:15:09 +1000 Subject: [PATCH 020/125] Revert to using guidProduct for all existing code, but the correct guidInstance for detecting XInput devices only. --- lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp index 04d568a36..21319c149 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp @@ -131,9 +131,9 @@ struct SJoystickWin32Control void directInputAddJoystick(LPCDIDEVICEINSTANCE lpddi) { //Get the GUID of the joystuck - const GUID guid = lpddi->guidProduct; + const GUID guid = lpddi->guidInstance; JoystickInfo activeJoystick; - activeJoystick.m_use_xinput = isXInputDevice(&guid); + activeJoystick.m_use_xinput = isXInputDevice(&lpddi->guidProduct); activeJoystick.Index=ActiveJoysticks.size(); activeJoystick.guid=guid; From 620d1f34c15e3598f8d169a110110106bc5bd896 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 7 Aug 2017 23:50:09 +1000 Subject: [PATCH 021/125] Link in xinput on windows builds. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 552f04ac0..6e1c9fcb1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -473,7 +473,7 @@ if(USE_WIIUSE) endif() if(MSVC OR MINGW) - target_link_libraries(supertuxkart iphlpapi.lib) + target_link_libraries(supertuxkart iphlpapi.lib xinput) add_custom_command(TARGET supertuxkart POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory "${PROJECT_SOURCE_DIR}/dependencies/dll" From 0a715ac31a45a964366642f35f210708182e1e14 Mon Sep 17 00:00:00 2001 From: Deve Date: Mon, 7 Aug 2017 22:33:57 +0200 Subject: [PATCH 022/125] Fixed 32-bit mingw compilation --- cmake/Toolchain-mingw.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Toolchain-mingw.cmake b/cmake/Toolchain-mingw.cmake index 141465228..052c984c0 100644 --- a/cmake/Toolchain-mingw.cmake +++ b/cmake/Toolchain-mingw.cmake @@ -10,7 +10,7 @@ SET(CMAKE_CXX_COMPILER i686-w64-mingw32-g++-posix) SET(CMAKE_RC_COMPILER i686-w64-mingw32-windres) # figure out folder to look in -execute_process(COMMAND sh -c "ls /usr/lib/gcc/x86_64-w64-mingw32/ | grep posix | tr -d '\n'" OUTPUT_VARIABLE MINGW_DEPS_FOLDER) +execute_process(COMMAND sh -c "ls /usr/lib/gcc/i686-w64-mingw32/ | grep posix | tr -d '\n'" OUTPUT_VARIABLE MINGW_DEPS_FOLDER) # here is the target environment located SET(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32 /usr/lib/gcc/i686-w64-mingw32/${MINGW_DEPS_FOLDER}/ ${PROJECT_SOURCE_DIR}/dependencies) From bd5eb89cc0557c3863448b04785fa46acccd1f03 Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 8 Aug 2017 21:44:32 +1000 Subject: [PATCH 023/125] Use XInput 9.1 which does not need an installer and works on most windows platforms. Move the linking to irrlicht cmake. --- CMakeLists.txt | 2 +- lib/irrlicht/CMakeLists.txt | 2 +- lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e1c9fcb1..552f04ac0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -473,7 +473,7 @@ if(USE_WIIUSE) endif() if(MSVC OR MINGW) - target_link_libraries(supertuxkart iphlpapi.lib xinput) + target_link_libraries(supertuxkart iphlpapi.lib) add_custom_command(TARGET supertuxkart POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory "${PROJECT_SOURCE_DIR}/dependencies/dll" diff --git a/lib/irrlicht/CMakeLists.txt b/lib/irrlicht/CMakeLists.txt index c7d649612..545e857ca 100644 --- a/lib/irrlicht/CMakeLists.txt +++ b/lib/irrlicht/CMakeLists.txt @@ -570,5 +570,5 @@ if(ENABLE_WAYLAND_DEVICE) endif() if(WIN32) - target_link_libraries(stkirrlicht imm32) + target_link_libraries(stkirrlicht imm32 XINPUT9_1_0.LIB) endif() diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp index 04d568a36..a2834c7ab 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp @@ -20,6 +20,10 @@ #include #include #if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) +// This define will switch to use XInput 9.1, which does not +// require an installer and works on most windows platforms. +// See https://blogs.msdn.microsoft.com/chuckw/2012/04/25/xinput-and-windows-8/ +#define _WIN32_WINNT 0x0601 #include #ifdef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ #define DIRECTINPUT_VERSION 0x0800 From b440511c955b90e50e604037a320207e9faed58e Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 8 Aug 2017 21:55:28 +1000 Subject: [PATCH 024/125] Fixed incorrect right thumbstick mapping. --- lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp index a2834c7ab..b38c04b59 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp @@ -401,8 +401,8 @@ void pollJoysticks() event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_X] = state.Gamepad.sThumbLX; event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Y] = ~state.Gamepad.sThumbLY; event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Z] = state.Gamepad.bLeftTrigger * 128; - event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_R] = state.Gamepad.sThumbRX; - event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_U] = ~state.Gamepad.sThumbRY; + event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_R] = ~state.Gamepad.sThumbRY; + event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_U] = state.Gamepad.sThumbRX; event.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_V] = state.Gamepad.bRightTrigger * 128; (void)Device->postEventFromUser(event); From f589bbea04732f1e2505db1df6646fef7a45070f Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Tue, 8 Aug 2017 20:23:22 -0400 Subject: [PATCH 025/125] Attempt to fix 2617 --- src/input/input_manager.cpp | 6 ++++-- src/input/input_manager.hpp | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/input/input_manager.cpp b/src/input/input_manager.cpp index f41aafab6..e604e6b82 100644 --- a/src/input/input_manager.cpp +++ b/src/input/input_manager.cpp @@ -488,11 +488,13 @@ void InputManager::inputSensing(Input::InputType type, int deviceID, // We have to save the direction in which the axis was moved. // This is done by storing it as a sign (and since button can // be zero, we add one before changing the sign). - int input_id = value>=0 ? 1+button : -(1+button); + int input_button_id = value>=0 ? 1+button : -(1+button); + std::tuple input_id(deviceID, input_button_id); + std::tuple input_id_inv(deviceID, -input_button_id); bool id_was_high = m_sensed_input_high_gamepad.find(input_id) != m_sensed_input_high_gamepad.end(); - bool inverse_id_was_high = m_sensed_input_high_gamepad.find(-input_id) + bool inverse_id_was_high = m_sensed_input_high_gamepad.find(input_id_inv) != m_sensed_input_high_gamepad.end(); bool id_was_zero = m_sensed_input_zero_gamepad.find(button) != m_sensed_input_zero_gamepad.end(); diff --git a/src/input/input_manager.hpp b/src/input/input_manager.hpp index 2ffff2ea4..8c2ed7ff0 100644 --- a/src/input/input_manager.hpp +++ b/src/input/input_manager.hpp @@ -53,7 +53,7 @@ public: private: DeviceManager *m_device_manager; - std::set m_sensed_input_high_gamepad; + std::set> m_sensed_input_high_gamepad; std::set m_sensed_input_high_kbd; std::set m_sensed_input_zero_gamepad; From 94bb657102cc7cb430916a35de67e60af54477ca Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Tue, 8 Aug 2017 20:44:31 -0400 Subject: [PATCH 026/125] Ignore input sensing coming from the wrong gamepad --- src/states_screens/options_screen_device.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/states_screens/options_screen_device.cpp b/src/states_screens/options_screen_device.cpp index caa3c43ea..8033226dc 100644 --- a/src/states_screens/options_screen_device.cpp +++ b/src/states_screens/options_screen_device.cpp @@ -434,6 +434,10 @@ void OptionsScreenDevice::gotSensedInput(const Input& sensed_input) // refresh display updateInputButtons(); } + else + { + return; + } } else if (sensed_input.m_type == Input::IT_NONE) { From ea97258b65e49f4bf7389944a1a0a1aa02bf02e8 Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 9 Aug 2017 12:23:37 +0800 Subject: [PATCH 027/125] Add scripting code for throwing banana monkeys --- lib/irrlicht/include/IAnimatedMeshSceneNode.h | 3 +- .../Irrlicht/CAnimatedMeshSceneNode.cpp | 55 ++++++++ .../source/Irrlicht/CAnimatedMeshSceneNode.h | 3 +- src/scriptengine/script_track.cpp | 73 +++++++++-- src/tracks/track_object.cpp | 37 +++++- src/tracks/track_object.hpp | 2 +- src/tracks/track_object_presentation.cpp | 120 ++++++++++-------- src/tracks/track_object_presentation.hpp | 26 ++-- 8 files changed, 241 insertions(+), 78 deletions(-) diff --git a/lib/irrlicht/include/IAnimatedMeshSceneNode.h b/lib/irrlicht/include/IAnimatedMeshSceneNode.h index 89c84d740..6929dd347 100644 --- a/lib/irrlicht/include/IAnimatedMeshSceneNode.h +++ b/lib/irrlicht/include/IAnimatedMeshSceneNode.h @@ -177,8 +177,9 @@ namespace scene \param newManager An optional new scene manager. \return The newly created clone of this node. */ virtual ISceneNode* clone(ISceneNode* newParent=0, ISceneManager* newManager=0) = 0; - + virtual void setFrameLoopOnce(s32 begin, s32 end) = 0; virtual u32 getAnimationSetNum() = 0; + virtual s32 getAnimationSet() const = 0; virtual void addAnimationSet(u32 start, u32 end) = 0; virtual void useAnimationSet(u32 set_num) = 0; }; diff --git a/lib/irrlicht/source/Irrlicht/CAnimatedMeshSceneNode.cpp b/lib/irrlicht/source/Irrlicht/CAnimatedMeshSceneNode.cpp index 3d6b08aa5..030b107e2 100644 --- a/lib/irrlicht/source/Irrlicht/CAnimatedMeshSceneNode.cpp +++ b/lib/irrlicht/source/Irrlicht/CAnimatedMeshSceneNode.cpp @@ -1002,5 +1002,60 @@ void CAnimatedMeshSceneNode::useAnimationSet(u32 set_num) setFrameLoop(m_animation_set[set_num * 2], m_animation_set[set_num * 2 + 1]); } +void CAnimatedMeshSceneNode::setFrameLoopOnce(s32 begin, s32 end) +{ + if (LoopCallBack != NULL || !Looping) + { + return; + } + Looping = false; + class MiniLoopSetter : public IAnimationEndCallBack + { + private: + int m_old_start, m_old_end, m_new_start, m_new_end; + + bool m_run_cb; + public: + MiniLoopSetter(int old_start, int old_end, int new_start, int new_end) + : m_old_start(old_start), m_old_end(old_end), + m_new_start(new_start), m_new_end(new_end), m_run_cb(false) {} + virtual void OnAnimationEnd(IAnimatedMeshSceneNode* node) + { + if (!m_run_cb) + { + m_run_cb = true; + node->setFrameLoop(m_new_start, m_new_end); + return; + } + if (m_run_cb) + { + node->setFrameLoop(m_old_start, m_old_end); + node->setLoopMode(true); + node->setAnimationEndCallback(NULL); + return; + } + } + }; + MiniLoopSetter* mls = new MiniLoopSetter(StartFrame, EndFrame, + begin, end); + setAnimationEndCallback(mls); + mls->drop(); + +} + +s32 CAnimatedMeshSceneNode::getAnimationSet() const +{ + for (u32 i = 0; i < m_animation_set.size(); i += 2) + { + if (m_animation_set[i] == (u32)StartFrame && + m_animation_set[i + 1] == (u32)EndFrame) + { + return (s32)(i >> 1); + } + } + return -1; +} + + } // end namespace scene } // end namespace irr diff --git a/lib/irrlicht/source/Irrlicht/CAnimatedMeshSceneNode.h b/lib/irrlicht/source/Irrlicht/CAnimatedMeshSceneNode.h index 1ea8f5112..68dc93748 100644 --- a/lib/irrlicht/source/Irrlicht/CAnimatedMeshSceneNode.h +++ b/lib/irrlicht/source/Irrlicht/CAnimatedMeshSceneNode.h @@ -161,13 +161,14 @@ namespace scene virtual ISceneNode* clone(ISceneNode* newParent=0, ISceneManager* newManager=0); virtual u32 getAnimationSetNum() { return m_animation_set.size() / 2; } + virtual s32 getAnimationSet() const; virtual void addAnimationSet(u32 start, u32 end) { m_animation_set.push_back(start); m_animation_set.push_back(end); } virtual void useAnimationSet(u32 set_num); - + virtual void setFrameLoopOnce(s32 begin, s32 end); protected: //! Get a static mesh for the current frame of this animated mesh diff --git a/src/scriptengine/script_track.cpp b/src/scriptengine/script_track.cpp index e4ad0e7cd..114657ba7 100644 --- a/src/scriptengine/script_track.cpp +++ b/src/scriptengine/script_track.cpp @@ -127,6 +127,23 @@ namespace Scripting #endif } + /** Function for re-enable a trigger after a specific timeout*/ + void setTriggerReenableTimeout(std::string* triggerID, std::string* lib_id, + float reenable_time) + { + ::TrackObject* tobj = ::Track::getCurrentTrack()->getTrackObjectManager() + ->getTrackObject(*lib_id, *triggerID); + if (tobj != NULL) + { + TrackObjectPresentationActionTrigger* topat = + tobj->getPresentation(); + if (topat != NULL) + { + topat->setReenableTimeout(reenable_time); + } + } + } + /** Exits the race to the main menu */ void exitRace() { @@ -231,22 +248,52 @@ namespace Scripting /** Sets a loop for a skeletal animation */ // TODO: can we use a type and avoid void* ? - void setLoop(int start, int end /** \cond DOXYGEN_IGNORE */, void *memory /** \endcond */) + void setFrameLoop(int start, int end /** \cond DOXYGEN_IGNORE */, void *memory /** \endcond */) { - ((TrackObjectPresentationMesh*)(memory))->setLoop(start, end); + if (memory) + { + ((scene::IAnimatedMeshSceneNode*)(memory))->setFrameLoop(start, end); + } + } + + /** Sets a loop once for a skeletal animation */ + void setFrameLoopOnce(int start, int end /** \cond DOXYGEN_IGNORE */, void *memory /** \endcond */) + { + if (memory) + { + ((scene::IAnimatedMeshSceneNode*)(memory))->setFrameLoopOnce(start, end); + } + } + + /** Get current frame in a skeletal animation */ + int getFrameNr(/** \cond DOXYGEN_IGNORE */void *memory /** \endcond */) + { + if (memory) + { + return ((scene::IAnimatedMeshSceneNode*)(memory))->getFrameNr(); + } + return -1; + } + + /** Gets the animation set for a skeletal animation */ + int getAnimationSet(/** \cond DOXYGEN_IGNORE */void *memory /** \endcond */) + { + if (memory) + { + return ((scene::IAnimatedMeshSceneNode*)(memory))->getAnimationSet(); + } + return -1; } /** Sets the current frame for a skeletal animation */ void setCurrentFrame(int frame /** \cond DOXYGEN_IGNORE */, void *memory /** \endcond */) { - ((TrackObjectPresentationMesh*)(memory))->setCurrentFrame(frame); + if (memory) + { + ((scene::IAnimatedMeshSceneNode*)(memory))->setCurrentFrame(frame); + } } - /** Get current frame in a skeletal animation */ - int getCurrentFrame(/** \cond DOXYGEN_IGNORE */void *memory /** \endcond */) - { - return ((TrackObjectPresentationMesh*)(memory))->getCurrentFrame(); - } /** @} */ } @@ -384,6 +431,8 @@ namespace Scripting asFUNCTION(createTrigger), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("void createTextBillboard(const string &in, const Vec3 &in)", asFUNCTION(createTextBillboard), asCALL_CDECL); assert(r >= 0); + r = engine->RegisterGlobalFunction("void setTriggerReenableTimeout(const string &in, const string &in, float reenable_time)", + asFUNCTION(setTriggerReenableTimeout), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("TrackObject@ getTrackObject(const string &in, const string &in)", asFUNCTION(getTrackObject), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("void exitRace()", asFUNCTION(exitRace), asCALL_CDECL); assert(r >= 0); r = engine->RegisterGlobalFunction("void pauseRace()", asFUNCTION(pauseRace), asCALL_CDECL); assert(r >= 0); @@ -410,9 +459,11 @@ namespace Scripting r = engine->RegisterObjectMethod("PhysicalObject", "void disable()", asMETHOD(PhysicalObject, disable), asCALL_THISCALL); assert(r >= 0); r = engine->RegisterObjectMethod("PhysicalObject", "void enable()", asMETHOD(PhysicalObject, enable), asCALL_THISCALL); assert(r >= 0); - // TrackObjectPresentationMesh (Mesh or Skeletal Animation) - r = engine->RegisterObjectMethod("Mesh", "void setLoop(int start, int end)", asFUNCTION(Mesh::setLoop), asCALL_CDECL_OBJLAST); assert(r >= 0); - r = engine->RegisterObjectMethod("Mesh", "int getCurrentFrame()", asFUNCTION(Mesh::getCurrentFrame), asCALL_CDECL_OBJLAST); assert(r >= 0); + // Animated Mesh + r = engine->RegisterObjectMethod("Mesh", "void setFrameLoop(int start, int end)", asFUNCTION(Mesh::setFrameLoop), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectMethod("Mesh", "void setFrameLoopOnce(int start, int end)", asFUNCTION(Mesh::setFrameLoopOnce), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectMethod("Mesh", "int getFrameNr()", asFUNCTION(Mesh::getFrameNr), asCALL_CDECL_OBJLAST); assert(r >= 0); + r = engine->RegisterObjectMethod("Mesh", "int getAnimationSet()", asFUNCTION(Mesh::getAnimationSet), asCALL_CDECL_OBJLAST); assert(r >= 0); r = engine->RegisterObjectMethod("Mesh", "void setCurrentFrame(int frame)", asFUNCTION(Mesh::setCurrentFrame), asCALL_CDECL_OBJLAST); assert(r >= 0); //r = engine->RegisterObjectMethod("Mesh", "void move(Vec3 &in)", asFUNCTION(movePresentation), asCALL_CDECL_OBJLAST); assert(r >= 0); diff --git a/src/tracks/track_object.cpp b/src/tracks/track_object.cpp index ada404ae6..f68dc3f0c 100644 --- a/src/tracks/track_object.cpp +++ b/src/tracks/track_object.cpp @@ -20,6 +20,7 @@ #include "animations/three_d_animation.hpp" #include "graphics/irr_driver.hpp" +#include "graphics/lod_node.hpp" #include "graphics/material.hpp" #include "graphics/material_manager.hpp" #include "graphics/render_info.hpp" @@ -32,6 +33,7 @@ #include "scriptengine/script_engine.hpp" #include "tracks/model_definition_loader.hpp" +#include #include /** A track object: any additional object on the track. This object implements @@ -156,6 +158,7 @@ void TrackObject::init(const XMLNode &xml_node, scene::ISceneNode* parent, } else if (xml_node.getName() == "library") { + xml_node.get("name", &m_name); m_presentation = new TrackObjectPresentationLibraryNode(this, xml_node, model_def_loader); } else if (type == "sfx-emitter") @@ -170,7 +173,7 @@ void TrackObject::init(const XMLNode &xml_node, scene::ISceneNode* parent, std::string action; xml_node.get("action", &action); m_name = action; //adds action as name so that it can be found by using getName() - m_presentation = new TrackObjectPresentationActionTrigger(xml_node); + m_presentation = new TrackObjectPresentationActionTrigger(xml_node, parent_library); } else if (type == "billboard") { @@ -661,3 +664,35 @@ void TrackObject::moveTo(const Scripting::SimpleVec3* pos, bool isAbsoluteCoord) isAbsoluteCoord); } } + +// ---------------------------------------------------------------------------- +scene::IAnimatedMeshSceneNode* TrackObject::getMesh() +{ + if (getPresentation()) + { + LODNode* ln = dynamic_cast + (getPresentation()->getNode()); + if (ln && !ln->getAllNodes().empty()) + { + scene::IAnimatedMeshSceneNode* an = + dynamic_cast + (ln->getFirstNode()); + if (an) + { + return an; + } + } + } + else if (getPresentation()) + { + scene::IAnimatedMeshSceneNode* an = + dynamic_cast + (getPresentation()->getNode()); + if (an) + { + return an; + } + } + Log::debug("TrackObject", "No animated mesh"); + return NULL; +} // getMesh diff --git a/src/tracks/track_object.hpp b/src/tracks/track_object.hpp index ceabc0c31..c8b7b5acc 100644 --- a/src/tracks/track_object.hpp +++ b/src/tracks/track_object.hpp @@ -190,7 +190,7 @@ public: /** Should only be used on mesh track objects. * On the script side, the returned object is of type : @ref Scripting_Mesh */ - TrackObjectPresentationMesh* getMesh() { return getPresentation(); } + scene::IAnimatedMeshSceneNode* getMesh(); /** Should only be used on particle emitter track objects. * On the script side, the returned object is of type : @ref Scripting_ParticleEmitter */ diff --git a/src/tracks/track_object_presentation.cpp b/src/tracks/track_object_presentation.cpp index 7a2914171..8f70e1ee6 100644 --- a/src/tracks/track_object_presentation.cpp +++ b/src/tracks/track_object_presentation.cpp @@ -358,11 +358,13 @@ void TrackObjectPresentationLOD::reset() dynamic_cast(node); if (a_node) { + a_node->setLoopMode(true); + a_node->setAnimationEndCallback(NULL); RandomGenerator rg; int animation_set = 0; if (a_node->getAnimationSetNum() > 0) animation_set = rg.get(a_node->getAnimationSetNum()); - a_node->useAnimationSet(animation_set); + a_node->useAnimationSet(animation_set); } } } @@ -642,6 +644,7 @@ void TrackObjectPresentationMesh::reset() a_node->setRotation(m_init_hpr); a_node->setScale(m_init_scale); a_node->setLoopMode(m_is_looped); + a_node->setAnimationEndCallback(NULL); a_node->setCurrentFrame((float)(a_node->getStartFrame())); // trick to reset the animation AND also the timer inside it @@ -658,49 +661,6 @@ void TrackObjectPresentationMesh::reset() } } // reset -// ---------------------------------------------------------------------------- -int TrackObjectPresentationMesh::getCurrentFrame() -{ - if (m_node->getType() == scene::ESNT_ANIMATED_MESH) - { - scene::IAnimatedMeshSceneNode *a_node = - (scene::IAnimatedMeshSceneNode*)m_node; - - return (int)a_node->getFrameNr(); - } - return -1; //Not a skeletal animation -} // getCurrentFrame - -// ---------------------------------------------------------------------------- -void TrackObjectPresentationMesh::setCurrentFrame(int frame) -{ - if (m_node->getType() == scene::ESNT_ANIMATED_MESH) - { - scene::IAnimatedMeshSceneNode *a_node = - (scene::IAnimatedMeshSceneNode*)m_node; - - a_node->setCurrentFrame((f32)frame); - } -} // setCurrentFrame - -// ---------------------------------------------------------------------------- -/** Set custom loops, as well as pause by scripts. - * \param start Start frame. - * \param end End frame. - */ -void TrackObjectPresentationMesh::setLoop(int start, int end) -{ - if (m_node->getType() == scene::ESNT_ANIMATED_MESH) - { - scene::IAnimatedMeshSceneNode *a_node = - (scene::IAnimatedMeshSceneNode*)m_node; - - // irrlicht's "setFrameLoop" is a misnomer, it just sets the first and - // last frame, even if looping is disabled - a_node->setFrameLoop(start, end); - } -} // setLoop - // ---------------------------------------------------------------------------- TrackObjectPresentationSound::TrackObjectPresentationSound( const XMLNode& xml_node, @@ -1094,7 +1054,8 @@ void TrackObjectPresentationLight::setEnergy(float energy) } // ---------------------------------------------------------------------------- TrackObjectPresentationActionTrigger::TrackObjectPresentationActionTrigger( - const XMLNode& xml_node) + const XMLNode& xml_node, + TrackObject* parent) : TrackObjectPresentation(xml_node) { float trigger_distance = 1.0f; @@ -1115,11 +1076,36 @@ TrackObjectPresentationActionTrigger::TrackObjectPresentationActionTrigger( { assert(false); } + m_xml_reenable_timeout = 999999.9f; + xml_node.get("reenable-timeout", &m_xml_reenable_timeout); + m_reenable_timeout = 0.0f; - m_action_active = true; - - if (m_action.size() == 0) + if (m_action.empty()) + { Log::warn("TrackObject", "Action-trigger has no action defined."); + return; + } + + if (parent != NULL) + { + core::vector3df parent_xyz = parent->getInitXYZ(); + core::vector3df parent_rot = parent->getInitRotation(); + core::vector3df parent_scale = parent->getInitScale(); + core::matrix4 lm, sm, rm; + lm.setTranslation(parent_xyz); + sm.setScale(parent_scale); + rm.setRotationDegrees(parent_rot); + core::matrix4 abs_trans = lm * rm * sm; + + m_library_id = parent->getID(); + m_library_name = parent->getName(); + xml_node.get("triggered-object", &m_triggered_object); + if (!m_library_id.empty() && !m_triggered_object.empty() && + !m_library_name.empty()) + { + abs_trans.transformVect(m_init_xyz); + } + } if (m_type == TRIGGER_TYPE_POINT) { @@ -1149,7 +1135,8 @@ TrackObjectPresentationActionTrigger::TrackObjectPresentationActionTrigger( m_init_scale = core::vector3df(1, 1, 1); float trigger_distance = distance; m_action = script_name; - m_action_active = true; + m_xml_reenable_timeout = 999999.9f; + m_reenable_timeout = 0.0f; m_type = TRIGGER_TYPE_POINT; ItemManager::get()->newItem(m_init_xyz, trigger_distance, this); } // TrackObjectPresentationActionTrigger @@ -1157,13 +1144,36 @@ TrackObjectPresentationActionTrigger::TrackObjectPresentationActionTrigger( // ---------------------------------------------------------------------------- void TrackObjectPresentationActionTrigger::onTriggerItemApproached() { - if (!m_action_active) return; + if (m_reenable_timeout > 0.0f) + { + return; + } + m_reenable_timeout = m_xml_reenable_timeout; - m_action_active = false; // TODO: allow auto re-activating? - int idKart = 0; + int kart_id = 0; Camera* camera = Camera::getActiveCamera(); if (camera != NULL && camera->getKart() != NULL) - idKart = camera->getKart()->getWorldKartId(); - Scripting::ScriptEngine::getInstance()->runFunction(true, "void " + m_action + "(int)", - [=](asIScriptContext* ctx) { ctx->SetArgDWord(0, idKart); }); + { + kart_id = camera->getKart()->getWorldKartId(); + } + if (!m_library_id.empty() && !m_triggered_object.empty() && + !m_library_name.empty()) + { + Scripting::ScriptEngine::getInstance()->runFunction(true, "void " + + m_library_name + "::" + m_action + + "(int, const string, const string)", [=](asIScriptContext* ctx) + { + ctx->SetArgDWord(0, kart_id); + ctx->SetArgObject(1, &m_library_id); + ctx->SetArgObject(2, &m_triggered_object); + }); + } + else + { + Scripting::ScriptEngine::getInstance()->runFunction(true, + "void " + m_action + "(int)", [=](asIScriptContext* ctx) + { + ctx->SetArgDWord(0, kart_id); + }); + } } // onTriggerItemApproached diff --git a/src/tracks/track_object_presentation.hpp b/src/tracks/track_object_presentation.hpp index 000ee5071..ed87ad248 100644 --- a/src/tracks/track_object_presentation.hpp +++ b/src/tracks/track_object_presentation.hpp @@ -251,9 +251,6 @@ public: const core::vector3df& hpr, const core::vector3df& scale); virtual ~TrackObjectPresentationMesh(); - void setLoop(int start, int end); - void setCurrentFrame(int frame); - int getCurrentFrame(); virtual void reset() OVERRIDE; // ------------------------------------------------------------------------ /** Returns the mode file name. */ @@ -382,14 +379,15 @@ class TrackObjectPresentationActionTrigger : public TrackObjectPresentation, { private: /** For action trigger objects */ - std::string m_action; + std::string m_action, m_library_id, m_triggered_object, m_library_name; - bool m_action_active; + float m_xml_reenable_timeout, m_reenable_timeout; ActionTriggerType m_type; public: - TrackObjectPresentationActionTrigger(const XMLNode& xml_node); + TrackObjectPresentationActionTrigger(const XMLNode& xml_node, + TrackObject* parent); TrackObjectPresentationActionTrigger(const core::vector3df& xyz, const std::string& scriptname, float distance); @@ -399,11 +397,23 @@ public: virtual void onTriggerItemApproached() OVERRIDE; // ------------------------------------------------------------------------ /** Reset the trigger (i.e. sets it to active again). */ - virtual void reset() OVERRIDE { m_action_active = true; } + virtual void reset() OVERRIDE { m_reenable_timeout = 0.0f; } + // ------------------------------------------------------------------------ + virtual void update(float dt) OVERRIDE + { + if (m_reenable_timeout < 900000.0f) + { + m_reenable_timeout -= dt; + } + } // ------------------------------------------------------------------------ /** Sets the trigger to be enabled or disabled. */ - virtual void setEnable(bool status) OVERRIDE{ m_action_active = status; } + virtual void setEnable(bool status) OVERRIDE + { m_reenable_timeout = status ? 0.0f : 999999.9f; } + // ------------------------------------------------------------------------ + void setReenableTimeout(float time) { m_reenable_timeout = time; } }; // class TrackObjectPresentationActionTrigger #endif // TRACKOBJECTPRESENTATION_HPP + From 79c6705bb14f1af96d47e1e2fdd73ea737190a66 Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 9 Aug 2017 13:00:09 +0800 Subject: [PATCH 028/125] Fix #2814 --- data/shaders/grass_pass.vert | 2 +- data/shaders/grass_pass2.frag | 21 +----------- data/shaders/instanced_grass.vert | 2 +- data/shaders/instanced_grass_pass2.frag | 25 ++------------ src/graphics/command_buffer.cpp | 20 ------------ src/graphics/command_buffer.hpp | 10 +----- src/graphics/draw_policies.cpp | 43 ------------------------- src/graphics/materials.cpp | 14 ++++---- src/graphics/materials.hpp | 4 +-- 9 files changed, 14 insertions(+), 127 deletions(-) diff --git a/data/shaders/grass_pass.vert b/data/shaders/grass_pass.vert index e6903c19e..c3c5d3721 100644 --- a/data/shaders/grass_pass.vert +++ b/data/shaders/grass_pass.vert @@ -20,7 +20,7 @@ out vec2 uv; void main() { - vec3 test = sin(windDir * (Position.y* 0.5)) * 0.5; + vec3 test = sin(windDir * (Position.y * 0.1)) * 1.; test += cos(windDir) * 0.7; mat4 new_model_matrix = ModelMatrix; diff --git a/data/shaders/grass_pass2.frag b/data/shaders/grass_pass2.frag index b5ebadbd9..c6d18a6c9 100644 --- a/data/shaders/grass_pass2.frag +++ b/data/shaders/grass_pass2.frag @@ -1,11 +1,9 @@ #ifdef Use_Bindless_Texture layout(bindless_sampler) uniform sampler2D Albedo; -layout(bindless_sampler) uniform sampler2D dtex; layout(bindless_sampler) uniform sampler2D SpecMap; layout(bindless_sampler) uniform sampler2D colorization_mask; #else uniform sampler2D Albedo; -uniform sampler2D dtex; uniform sampler2D SpecMap; uniform sampler2D colorization_mask; #endif @@ -36,25 +34,8 @@ void main(void) vec2 new_xy = mix(vec2(old_hsv.x, old_hsv.y), vec2(color_change.x, max(old_hsv.y, color_change.y)), vec2(mask_step, mask_step)); color.xyz = hsvToRgb(vec3(new_xy.x, new_xy.y, old_hsv.z)); } - - vec2 texc = gl_FragCoord.xy / screen; - float z = texture(dtex, texc).x; - - vec4 xpos = 2.0 * vec4(texc, z, 1.0) - 1.0f; - xpos = InverseProjectionMatrix * xpos; - xpos /= xpos.w; - vec3 eyedir = normalize(xpos.xyz); - - // Inspired from http://http.developer.nvidia.com/GPUGems3/gpugems3_ch16.html - vec3 L = normalize((transpose(InverseViewMatrix) * vec4(sun_direction, 0.)).xyz); - float fEdotL = clamp(dot(L, eyedir), 0., 1.); - float fPowEdotL = pow(fEdotL, 4.); - - float fLdotNBack = max(0., - dot(nor, L) * 0.6 + 0.4); - float scattering = mix(fPowEdotL, fLdotNBack, .5); - float specmap = texture(SpecMap, uv).g; float emitmap = texture(SpecMap, uv).b; - vec3 LightFactor = color.xyz * (scattering * 0.1) + getLightFactor(color.xyz, vec3(1.), specmap, emitmap); + vec3 LightFactor = getLightFactor(color.xyz, vec3(1.), specmap, emitmap); FragColor = vec4(LightFactor, 1.); } diff --git a/data/shaders/instanced_grass.vert b/data/shaders/instanced_grass.vert index 0127a8346..92cac1e4f 100644 --- a/data/shaders/instanced_grass.vert +++ b/data/shaders/instanced_grass.vert @@ -41,7 +41,7 @@ flat out sampler2D thirdhandle; void main() { - vec3 test = sin(windDir * (Position.y* 0.5)) * 0.5; + vec3 test = sin(windDir * (Position.y * 0.1)) * 1.; test += cos(windDir) * 0.7; mat4 ModelMatrix = getWorldMatrix(Origin + test * Color.r, Orientation, Scale); mat4 TransposeInverseModelView = transpose(getInverseWorldMatrix(Origin + test * Color.r, Orientation, Scale) * InverseViewMatrix); diff --git a/data/shaders/instanced_grass_pass2.frag b/data/shaders/instanced_grass_pass2.frag index 6ba698f7b..0577f3f75 100644 --- a/data/shaders/instanced_grass_pass2.frag +++ b/data/shaders/instanced_grass_pass2.frag @@ -1,9 +1,6 @@ -#ifdef Use_Bindless_Texture -layout(bindless_sampler) uniform sampler2D dtex; -#else +#ifndef Use_Bindless_Texture uniform sampler2D Albedo; uniform sampler2D SpecMap; -uniform sampler2D dtex; uniform sampler2D colorization_mask; #endif @@ -45,24 +42,6 @@ void main(void) vec2 new_xy = mix(vec2(old_hsv.x, old_hsv.y), vec2(color_change.x, max(old_hsv.y, color_change.y)), vec2(mask_step, mask_step)); color.xyz = hsvToRgb(vec3(new_xy.x, new_xy.y, old_hsv.z)); } - - vec2 texc = gl_FragCoord.xy / screen; - float z = texture(dtex, texc).x; - - vec4 xpos = 2.0 * vec4(texc, z, 1.0) - 1.0f; - xpos = InverseProjectionMatrix * xpos; - xpos /= xpos.w; - vec3 eyedir = normalize(xpos.xyz); - - // Inspired from http://http.developer.nvidia.com/GPUGems3/gpugems3_ch16.html - vec3 L = normalize((transpose(InverseViewMatrix) * vec4(sun_direction, 0.)).xyz); - float fEdotL = clamp(dot(L, eyedir), 0., 1.); - float fPowEdotL = pow(fEdotL, 4.); - - float fLdotNBack = max(0., - dot(nor, L) * 0.6 + 0.4); - float scattering = mix(fPowEdotL, fLdotNBack, .5); - - vec3 LightFactor = color.xyz * (scattering * 0.1) + getLightFactor(color.xyz, vec3(1.), specmap, emitmap); - + vec3 LightFactor = getLightFactor(color.xyz, vec3(1.), specmap, emitmap); FragColor = vec4(LightFactor, 1.); } diff --git a/src/graphics/command_buffer.cpp b/src/graphics/command_buffer.cpp index 24df0d6cb..761ae122b 100644 --- a/src/graphics/command_buffer.cpp +++ b/src/graphics/command_buffer.cpp @@ -80,26 +80,6 @@ void InstanceFiller::add(GLMesh* mesh, instance.Color = nd->getGlowColor().color; } -// ---------------------------------------------------------------------------- -template<> -void expandTexSecondPass(const GLMesh &mesh, - const std::vector &prefilled_tex) -{ - TexExpander:: - expandTex(mesh, GrassMat::SecondPassTextures, prefilled_tex[0], - prefilled_tex[1], prefilled_tex[2], prefilled_tex[3]); -} - -// ---------------------------------------------------------------------------- -template<> -void expandHandlesSecondPass(const std::vector &handles) -{ - uint64_t nulltex[10] = {}; - HandleExpander:: - expand(nulltex, GrassMat::SecondPassTextures, - handles[0], handles[1], handles[2], handles[3]); -} - #if !defined(USE_GLES2) // ---------------------------------------------------------------------------- template diff --git a/src/graphics/command_buffer.hpp b/src/graphics/command_buffer.hpp index 565b2c7b2..c1e7543bd 100644 --- a/src/graphics/command_buffer.hpp +++ b/src/graphics/command_buffer.hpp @@ -112,11 +112,7 @@ void expandTexSecondPass(const GLMesh &mesh, expandTex(mesh, T::SecondPassTextures, prefilled_tex[0], prefilled_tex[1], prefilled_tex[2]); } - -template<> -void expandTexSecondPass(const GLMesh &mesh, - const std::vector &prefilled_tex); - + // ---------------------------------------------------------------------------- /** Give acces textures for second rendering pass in shaders * without first binding them in order to reduce driver overhead. @@ -133,10 +129,6 @@ void expandHandlesSecondPass(const std::vector &handles) handles[0], handles[1], handles[2]); } -template<> -void expandHandlesSecondPass(const std::vector &handles); - - #if !defined(USE_GLES2) // ---------------------------------------------------------------------------- /** diff --git a/src/graphics/draw_policies.cpp b/src/graphics/draw_policies.cpp index 9414caa8c..b9e593f2e 100644 --- a/src/graphics/draw_policies.cpp +++ b/src/graphics/draw_policies.cpp @@ -89,49 +89,6 @@ void renderMeshes2ndPass( const std::vector &Prefilled_Handle, } } // renderMeshes2ndPass -// ---------------------------------------------------------------------------- -template<> -void renderMeshes2ndPass - (const std::vector &Prefilled_Handle, - const std::vector &Prefilled_Tex) -{ - auto &meshes = GrassMat::List::getInstance()->SolidPass; - GrassMat::SecondPassShader::getInstance()->use(); - if (CVS->isARBBaseInstanceUsable()) - glBindVertexArray(VAOManager::getInstance()->getVAO(GrassMat::VertexType)); - for (unsigned i = 0; i < meshes.size(); i++) - { - GLMesh &mesh = *(std::get<0>(meshes.at(i))); - if (!CVS->isARBBaseInstanceUsable()) - glBindVertexArray(mesh.vao); - - if (mesh.VAOType != GrassMat::VertexType) - { -#ifdef DEBUG - Log::error("Materials", "Wrong vertex Type associed to pass 2 " - "(hint texture : %s)", - mesh.textures[0]->getName().getPath().c_str()); -#endif - continue; - } - - if (CVS->isAZDOEnabled()) - { - HandleExpander:: - expand(mesh.TextureHandles, GrassMat::SecondPassTextures, - Prefilled_Handle[0], Prefilled_Handle[1], - Prefilled_Handle[2], Prefilled_Handle[3]); - } - else - { - TexExpander:: - expandTex(mesh, GrassMat::SecondPassTextures, Prefilled_Tex[0], - Prefilled_Tex[1], Prefilled_Tex[2], Prefilled_Tex[3]); - } - CustomUnrollArgs<4, 3, 1>::drawMesh(meshes.at(i)); - } -} // renderMeshes2ndPass - // ---------------------------------------------------------------------------- template void renderShadow(unsigned cascade) diff --git a/src/graphics/materials.cpp b/src/graphics/materials.cpp index e7b098071..681f43b76 100644 --- a/src/graphics/materials.cpp +++ b/src/graphics/materials.cpp @@ -432,10 +432,9 @@ GrassPass2Shader::GrassPass2Shader() assignSamplerNames(0, "DiffuseMap", ST_NEAREST_FILTERED, 1, "SpecularMap", ST_NEAREST_FILTERED, 2, "SSAO", ST_BILINEAR_FILTERED, - 3, "dtex", ST_NEAREST_FILTERED, - 4, "Albedo", ST_TRILINEAR_ANISOTROPIC_FILTERED, - 5, "SpecMap", ST_TRILINEAR_ANISOTROPIC_FILTERED, - 6, "colorization_mask", + 3, "Albedo", ST_TRILINEAR_ANISOTROPIC_FILTERED, + 4, "SpecMap", ST_TRILINEAR_ANISOTROPIC_FILTERED, + 5, "colorization_mask", ST_TRILINEAR_ANISOTROPIC_FILTERED); } // GrassPass2Shader @@ -448,10 +447,9 @@ InstancedGrassPass2Shader::InstancedGrassPass2Shader() assignSamplerNames(0, "DiffuseMap", ST_NEAREST_FILTERED, 1, "SpecularMap", ST_NEAREST_FILTERED, 2, "SSAO", ST_BILINEAR_FILTERED, - 3, "dtex", ST_NEAREST_FILTERED, - 4, "Albedo", ST_TRILINEAR_ANISOTROPIC_FILTERED, - 5, "SpecMap", ST_TRILINEAR_ANISOTROPIC_FILTERED, - 6, "colorization_mask", + 3, "Albedo", ST_TRILINEAR_ANISOTROPIC_FILTERED, + 4, "SpecMap", ST_TRILINEAR_ANISOTROPIC_FILTERED, + 5, "colorization_mask", ST_TRILINEAR_ANISOTROPIC_FILTERED); } // InstancedGrassPass2Shader diff --git a/src/graphics/materials.hpp b/src/graphics/materials.hpp index 4bc52bd82..8cce5a087 100644 --- a/src/graphics/materials.hpp +++ b/src/graphics/materials.hpp @@ -260,7 +260,7 @@ public: }; // InstancedGrassShadowShader // ============================================================================ -class GrassPass2Shader : public TextureShader { @@ -270,7 +270,7 @@ public: // ============================================================================ class InstancedGrassPass2Shader - : public TextureShader + : public TextureShader { public: InstancedGrassPass2Shader(); From cc8331f5cdf8a51b473fcb71b7d34f487478cb51 Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 9 Aug 2017 15:03:37 +0800 Subject: [PATCH 029/125] Fix #2897 --- data/shaders/billboard.frag | 2 ++ data/shaders/billboard.vert | 6 ++-- src/graphics/shader_based_renderer.cpp | 2 ++ src/graphics/stk_billboard.cpp | 49 ++++++++++++++++++-------- src/graphics/stk_billboard.hpp | 5 +++ 5 files changed, 48 insertions(+), 16 deletions(-) diff --git a/data/shaders/billboard.frag b/data/shaders/billboard.frag index 7b7af00a8..6f583946a 100644 --- a/data/shaders/billboard.frag +++ b/data/shaders/billboard.frag @@ -1,10 +1,12 @@ uniform sampler2D tex; in vec2 uv; +in vec4 vertex_color; out vec4 FragColor; void main(void) { vec4 color = texture(tex, uv); + color *= vertex_color; FragColor = vec4(color.a * color.rgb, color.a); } diff --git a/data/shaders/billboard.vert b/data/shaders/billboard.vert index 06882526d..c47b2f15a 100644 --- a/data/shaders/billboard.vert +++ b/data/shaders/billboard.vert @@ -1,4 +1,4 @@ -uniform mat4 ModelViewMatrix; +uniform mat4 color_matrix; uniform vec3 Position; uniform vec2 Size; @@ -11,10 +11,12 @@ in vec2 Texcoord; #endif out vec2 uv; +out vec4 vertex_color; void main(void) { uv = Texcoord; - vec4 Center = ModelViewMatrix * vec4(Position, 1.); + vec4 Center = ViewMatrix * vec4(Position, 1.); gl_Position = ProjectionMatrix * (Center + vec4(Size * Corner, 0., 0.)); + vertex_color = color_matrix[gl_VertexID]; } diff --git a/src/graphics/shader_based_renderer.cpp b/src/graphics/shader_based_renderer.cpp index 1d253cb58..8a86c2464 100644 --- a/src/graphics/shader_based_renderer.cpp +++ b/src/graphics/shader_based_renderer.cpp @@ -33,6 +33,7 @@ #include "graphics/rtts.hpp" #include "graphics/shaders.hpp" #include "graphics/skybox.hpp" +#include "graphics/stk_billboard.hpp" #include "graphics/stk_mesh_scene_node.hpp" #include "graphics/spherical_harmonics.hpp" #include "items/item_manager.hpp" @@ -659,6 +660,7 @@ ShaderBasedRenderer::~ShaderBasedRenderer() delete m_skybox; delete m_rtts; ShaderFilesManager::kill(); + STKBillboard::destroyBillboardVAO(); } // ---------------------------------------------------------------------------- diff --git a/src/graphics/stk_billboard.cpp b/src/graphics/stk_billboard.cpp index 2a3d99ca8..53c0cb39c 100644 --- a/src/graphics/stk_billboard.cpp +++ b/src/graphics/stk_billboard.cpp @@ -30,10 +30,10 @@ using namespace irr; -static GLuint billboardvao = 0; +GLuint STKBillboard::m_billboard_vao = 0; -class BillboardShader : public TextureShader { @@ -43,18 +43,16 @@ public: loadProgram(OBJECT, GL_VERTEX_SHADER, "billboard.vert", GL_FRAGMENT_SHADER, "billboard.frag"); - assignUniforms("ModelViewMatrix", "ProjectionMatrix", "Position", - "Size"); + assignUniforms("color_matrix", "Position", "Size"); assignSamplerNames(0, "tex", ST_TRILINEAR_ANISOTROPIC_FILTERED); } // BillboardShader }; // BillboardShader // ============================================================================ - -static void createBillboardVAO() +void STKBillboard::createBillboardVAO() { - glGenVertexArrays(1, &billboardvao); - glBindVertexArray(billboardvao); + glGenVertexArrays(1, &m_billboard_vao); + glBindVertexArray(m_billboard_vao); glBindBuffer(GL_ARRAY_BUFFER, SharedGPUObjects::getBillboardVBO()); glEnableVertexAttribArray(0); glEnableVertexAttribArray(3); @@ -64,6 +62,16 @@ static void createBillboardVAO() glBindVertexArray(0); } // createBillboardVAO +// ---------------------------------------------------------------------------- +void STKBillboard::destroyBillboardVAO() +{ + if (m_billboard_vao != 0) + { + glDeleteVertexArrays(1, &m_billboard_vao); + m_billboard_vao = 0; + } +} // destroyBillboardVAO + // ---------------------------------------------------------------------------- STKBillboard::STKBillboard(irr::scene::ISceneNode* parent, irr::scene::ISceneManager* mgr, irr::s32 id, @@ -75,7 +83,7 @@ STKBillboard::STKBillboard(irr::scene::ISceneNode* parent, CBillboardSceneNode(parent, mgr, id, position, size, colorTop, colorBottom) { - if (!billboardvao) + if (!m_billboard_vao) createBillboardVAO(); } // STKBillboard @@ -97,9 +105,9 @@ void STKBillboard::render() return; core::vector3df pos = getAbsolutePosition(); - glBindVertexArray(billboardvao); + glBindVertexArray(m_billboard_vao); video::ITexture *tex = Material.getTexture(0); - if (!tex ) + if (!tex) return; ::Material* material = material_manager->getMaterialFor(tex, @@ -109,11 +117,24 @@ void STKBillboard::render() else glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + video::SColor col[2]; + getColor(col[0], col[1]); + const float colors[] = + { + col[1].getRed() / 255.f, col[1].getGreen() / 255.f, + col[1].getBlue() / 255.f, col[1].getAlpha() / 255.f, + col[0].getRed() / 255.f, col[0].getGreen() / 255.f, + col[0].getBlue() / 255.f, col[0].getAlpha() / 255.f, + col[1].getRed() / 255.f, col[1].getGreen() / 255.f, + col[1].getBlue() / 255.f, col[1].getAlpha() / 255.f, + col[0].getRed() / 255.f, col[0].getGreen() / 255.f, + col[0].getBlue() / 255.f, col[0].getAlpha() / 255.f, + }; + core::matrix4 color_matrix; + color_matrix.setM(colors); BillboardShader::getInstance()->use(); BillboardShader::getInstance()->setTextureUnits(tex->getOpenGLTextureName()); - BillboardShader::getInstance()->setUniforms(irr_driver->getViewMatrix(), - irr_driver->getProjMatrix(), - pos, Size); + BillboardShader::getInstance()->setUniforms(color_matrix, pos, Size); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glBindVertexArray(0); } // render diff --git a/src/graphics/stk_billboard.hpp b/src/graphics/stk_billboard.hpp index 63ef48d4d..b8a8be723 100644 --- a/src/graphics/stk_billboard.hpp +++ b/src/graphics/stk_billboard.hpp @@ -21,10 +21,14 @@ #include "../lib/irrlicht/source/Irrlicht/CBillboardSceneNode.h" #include #include +#include "graphics/gl_headers.hpp" #include "utils/cpp2011.hpp" class STKBillboard : public irr::scene::CBillboardSceneNode { +private: + static GLuint m_billboard_vao; + static void createBillboardVAO(); public: STKBillboard(irr::scene::ISceneNode* parent, irr::scene::ISceneManager* mgr, irr::s32 id, const irr::core::vector3df& position, @@ -35,6 +39,7 @@ public: virtual void OnRegisterSceneNode() OVERRIDE; virtual void render() OVERRIDE; + static void destroyBillboardVAO(); }; // STKBillboard #endif From 861e7f7ebd885a1bb19d795c2dbe98ae1c2675fc Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 9 Aug 2017 15:36:26 +0800 Subject: [PATCH 030/125] Remove contradiction --- src/karts/kart_model.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/karts/kart_model.cpp b/src/karts/kart_model.cpp index ebd1fb17c..111eab374 100644 --- a/src/karts/kart_model.cpp +++ b/src/karts/kart_model.cpp @@ -273,7 +273,6 @@ KartModel::~KartModel() for (size_t i = 0; i < m_headlight_objects.size(); i++) { HeadlightObject& obj = m_headlight_objects[i]; - obj.setNode(NULL); if (obj.getNode()) { // Master KartModels should never have a headlight attached. From d46b03b317de7c19ffcb58592919864402a96e15 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 9 Aug 2017 17:54:29 +1000 Subject: [PATCH 031/125] Hopefully fixed cygwin build problems. --- lib/irrlicht/CMakeLists.txt | 2 +- lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/irrlicht/CMakeLists.txt b/lib/irrlicht/CMakeLists.txt index 545e857ca..89de9cf10 100644 --- a/lib/irrlicht/CMakeLists.txt +++ b/lib/irrlicht/CMakeLists.txt @@ -570,5 +570,5 @@ if(ENABLE_WAYLAND_DEVICE) endif() if(WIN32) - target_link_libraries(stkirrlicht imm32 XINPUT9_1_0.LIB) + target_link_libraries(stkirrlicht imm32 XINPUT9_1_0) endif() diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp index a5d0644db..32863337c 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp @@ -23,8 +23,14 @@ // This define will switch to use XInput 9.1, which does not // require an installer and works on most windows platforms. // See https://blogs.msdn.microsoft.com/chuckw/2012/04/25/xinput-and-windows-8/ +#ifdef __CYGWIN__ +#define _WIN32_WINNT 0x0601 +#include +#else +#undef _WIN32_WINNT #define _WIN32_WINNT 0x0601 #include +#endif #ifdef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ #define DIRECTINPUT_VERSION 0x0800 #include From e03c0849ce908b1b3ba8c093118c0266de487b1a Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 9 Aug 2017 20:30:44 +1000 Subject: [PATCH 032/125] Fixed previous commit - got confused with the #undef :P --- lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp index 32863337c..90fc86c1e 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp @@ -23,12 +23,11 @@ // This define will switch to use XInput 9.1, which does not // require an installer and works on most windows platforms. // See https://blogs.msdn.microsoft.com/chuckw/2012/04/25/xinput-and-windows-8/ -#ifdef __CYGWIN__ -#define _WIN32_WINNT 0x0601 -#include -#else #undef _WIN32_WINNT #define _WIN32_WINNT 0x0601 +#ifdef __CYGWIN__ +#include +#else #include #endif #ifdef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ From 936ef3c4c732aaa126a116d1ed853ec3f952cab4 Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Wed, 9 Aug 2017 19:37:49 -0400 Subject: [PATCH 033/125] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f12f5b50..ba326e73e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * Hardware skinning * New smoother camera by Auria * New grand prix win scene +* Gamepad configuration bugfixes * Various improvements (wall driving fixes, parachutes, GP points, cannon fixes, colorization shader) ## SuperTuxKart 0.9.2 From 5b68e18df412629fe5e57f9484b995965a58e21f Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 11 Aug 2017 09:38:58 +1000 Subject: [PATCH 034/125] Fix cygwin support. --- lib/irrlicht/CMakeLists.txt | 2 +- lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/irrlicht/CMakeLists.txt b/lib/irrlicht/CMakeLists.txt index 89de9cf10..ae2b46f41 100644 --- a/lib/irrlicht/CMakeLists.txt +++ b/lib/irrlicht/CMakeLists.txt @@ -570,5 +570,5 @@ if(ENABLE_WAYLAND_DEVICE) endif() if(WIN32) - target_link_libraries(stkirrlicht imm32 XINPUT9_1_0) + target_link_libraries(stkirrlicht imm32 xinput9_1_0) endif() diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp index 90fc86c1e..ffead1982 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceWin32.cpp @@ -25,11 +25,8 @@ // See https://blogs.msdn.microsoft.com/chuckw/2012/04/25/xinput-and-windows-8/ #undef _WIN32_WINNT #define _WIN32_WINNT 0x0601 -#ifdef __CYGWIN__ +// Don't change the case of xinput.h, otherwise cygwin compilation fails. #include -#else -#include -#endif #ifdef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ #define DIRECTINPUT_VERSION 0x0800 #include From 3923da30bcbaa33160297280cf825dafd8d53dd6 Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 11 Aug 2017 09:41:22 +1000 Subject: [PATCH 035/125] Use unique names for profiler events. --- src/main_loop.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main_loop.cpp b/src/main_loop.cpp index 6f97697f2..446320c0e 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -256,7 +256,7 @@ void MainLoop::run() // enabled. if (!m_abort && !ProfileWorld::isNoGraphics()) { - PROFILER_PUSH_CPU_MARKER("Music/input/GUI", 0x7F, 0x00, 0x00); + PROFILER_PUSH_CPU_MARKER("Input/GUI", 0x7F, 0x00, 0x00); input_manager->update(dt); #ifdef ENABLE_WIIUSE @@ -269,7 +269,7 @@ void MainLoop::run() // Update sfx and music after graphics, so that graphics code // can use as many threads as possible without interfering // with audio - PROFILER_PUSH_CPU_MARKER("Music/input/GUI", 0x7F, 0x00, 0x00); + PROFILER_PUSH_CPU_MARKER("Music", 0x7F, 0x00, 0x00); SFXManager::get()->update(); PROFILER_POP_CPU_MARKER(); From cf868df93f628e56301b0064dffd32c469fd3bb1 Mon Sep 17 00:00:00 2001 From: Deve Date: Sun, 13 Aug 2017 01:03:13 +0200 Subject: [PATCH 036/125] Fixed memory leaks in wayland device --- .../source/Irrlicht/CIrrDeviceWayland.cpp | 44 ++++++++++++++++--- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceWayland.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceWayland.cpp index 8493ecf5a..6ceff83f3 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceWayland.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceWayland.cpp @@ -715,6 +715,9 @@ CIrrDeviceWayland::CIrrDeviceWayland(const SIrrlichtCreationParameters& params) CIrrDeviceWayland::~CIrrDeviceWayland() { delete m_egl_context; + + if (m_egl_window) + wl_egl_window_destroy(m_egl_window); if (m_keyboard) wl_keyboard_destroy(m_keyboard); @@ -730,15 +733,46 @@ CIrrDeviceWayland::~CIrrDeviceWayland() if (m_shell_surface) wl_shell_surface_destroy(m_shell_surface); + + if (m_surface) + wl_surface_destroy(m_surface); + + if (m_shell) + wl_shell_destroy(m_shell); + + if (m_shm) + wl_shm_destroy(m_shm); + + if (m_compositor) + wl_compositor_destroy(m_compositor); + + if (m_output) + wl_output_destroy(m_output); + + if (m_seat) + wl_seat_destroy(m_seat); + + if (m_registry) + wl_registry_destroy(m_registry); + + if (m_xkb_state) + xkb_state_unref(m_xkb_state); + + if (m_xkb_keymap) + xkb_keymap_unref(m_xkb_keymap); + + if (m_xkb_compose_state) + xkb_compose_state_unref(m_xkb_compose_state); + + if (m_xkb_compose_table) + xkb_compose_table_unref(m_xkb_compose_table); + + if (m_xkb_context) + xkb_context_unref(m_xkb_context); - wl_output_destroy(m_output); - wl_seat_destroy(m_seat); - wl_registry_destroy(m_registry); wl_display_flush(m_display); wl_display_disconnect(m_display); - xkb_context_unref(m_xkb_context); - closeJoysticks(); } From fd4ab9d88f94c794f349c454d3c0012bdd0bc9c3 Mon Sep 17 00:00:00 2001 From: Deve Date: Sun, 13 Aug 2017 01:04:19 +0200 Subject: [PATCH 037/125] Fixed possible uninitialized variable in gles renderer --- lib/irrlicht/source/Irrlicht/COGLES2MaterialRenderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/irrlicht/source/Irrlicht/COGLES2MaterialRenderer.cpp b/lib/irrlicht/source/Irrlicht/COGLES2MaterialRenderer.cpp index fea967bee..a068dbf3f 100644 --- a/lib/irrlicht/source/Irrlicht/COGLES2MaterialRenderer.cpp +++ b/lib/irrlicht/source/Irrlicht/COGLES2MaterialRenderer.cpp @@ -93,7 +93,7 @@ COGLES2MaterialRenderer::~COGLES2MaterialRenderer() if (Program) { GLuint shaders[8]; - GLint count; + GLint count = 0; glGetAttachedShaders(Program, 8, &count, shaders); count=core::min_(count,8); From ee5bdad9260d6eb65245f6fed0529334f0e61e95 Mon Sep 17 00:00:00 2001 From: Deve Date: Sun, 13 Aug 2017 01:12:00 +0200 Subject: [PATCH 038/125] Move output/seat listener to better place --- lib/irrlicht/source/Irrlicht/CIrrDeviceWayland.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceWayland.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceWayland.cpp index 6ceff83f3..0c504d84f 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceWayland.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceWayland.cpp @@ -537,6 +537,7 @@ public: device->m_seat = static_cast(wl_registry_bind(registry, name, &wl_seat_interface, version < 4 ? version : 4)); + wl_seat_add_listener(device->m_seat, &seat_listener, device); } else if (interface_str == "wl_shm") { @@ -547,6 +548,7 @@ public: { device->m_output = static_cast(wl_registry_bind(registry, name, &wl_output_interface, 2)); + wl_output_add_listener(device->m_output, &output_listener, device); } } @@ -700,9 +702,6 @@ CIrrDeviceWayland::CIrrDeviceWayland(const SIrrlichtCreationParameters& params) return; } - wl_seat_add_listener(m_seat, &WaylandCallbacks::seat_listener, this); - wl_output_add_listener(m_output, &WaylandCallbacks::output_listener, this); - createDriver(); if (VideoDriver) From 1f4a9135b63e2c7fd258c0e91f23d2b02ed98998 Mon Sep 17 00:00:00 2001 From: Deve Date: Sun, 13 Aug 2017 01:20:22 +0200 Subject: [PATCH 039/125] Remove useless elif --- src/graphics/shader_based_renderer.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/graphics/shader_based_renderer.cpp b/src/graphics/shader_based_renderer.cpp index 8a86c2464..0a6aca980 100644 --- a/src/graphics/shader_based_renderer.cpp +++ b/src/graphics/shader_based_renderer.cpp @@ -59,12 +59,9 @@ void ShaderBasedRenderer::setRTT(RTT* rtts) rtts->getDepthStencilTexture()); m_geometry_passes->setFirstPassRenderTargets(prefilled_textures, rtts->getPrefilledHandles()); - m_rtts = rtts; - } - else if (rtts == NULL) - { - m_rtts = NULL; } + + m_rtts = rtts; } //setRTT // ---------------------------------------------------------------------------- From 0bf0ca0a35b31e5161a60cb09e8c700af1ffd57a Mon Sep 17 00:00:00 2001 From: Deve Date: Sun, 13 Aug 2017 01:34:43 +0200 Subject: [PATCH 040/125] Fixed memory leak --- src/input/device_manager.cpp | 6 ++++++ src/input/device_manager.hpp | 1 + 2 files changed, 7 insertions(+) diff --git a/src/input/device_manager.cpp b/src/input/device_manager.cpp index e5fc6f144..4ea2b0059 100644 --- a/src/input/device_manager.cpp +++ b/src/input/device_manager.cpp @@ -47,6 +47,12 @@ DeviceManager::DeviceManager() m_multitouch_device = NULL; } // DeviceManager +// ----------------------------------------------------------------------------- +DeviceManager::~DeviceManager() +{ + delete m_multitouch_device; +} // ~DeviceManager + // ----------------------------------------------------------------------------- bool DeviceManager::initialize() { diff --git a/src/input/device_manager.hpp b/src/input/device_manager.hpp index 341ebaf5f..e9e0388cc 100644 --- a/src/input/device_manager.hpp +++ b/src/input/device_manager.hpp @@ -103,6 +103,7 @@ public: DeviceManager(); + ~DeviceManager(); // ---- Assign mode ---- PlayerAssignMode getAssignMode() const { return m_assign_mode; } From 71bbafadbd35a98921208c3fa74a7fd99941df0c Mon Sep 17 00:00:00 2001 From: Deve Date: Sun, 13 Aug 2017 22:02:55 +0200 Subject: [PATCH 041/125] Fixed memory leaks in gles --- lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp | 3 +++ lib/irrlicht/source/Irrlicht/COGLES2Driver.cpp | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp index 85dd171f8..4f4ed1358 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp @@ -1218,6 +1218,9 @@ bool CIrrDeviceLinux::createInputContext() return false; } + // It's showed as memory leak, but we shouldn't delete it. From the xlib + // documentation: "The returned modifiers string is owned by Xlib and + // should not be modified or freed by the client." char* p = XSetLocaleModifiers(""); if (p == NULL) { diff --git a/lib/irrlicht/source/Irrlicht/COGLES2Driver.cpp b/lib/irrlicht/source/Irrlicht/COGLES2Driver.cpp index 77890ecf5..7b86362e7 100644 --- a/lib/irrlicht/source/Irrlicht/COGLES2Driver.cpp +++ b/lib/irrlicht/source/Irrlicht/COGLES2Driver.cpp @@ -432,6 +432,13 @@ namespace video addAndDropMaterialRenderer(new COGLES2ParallaxMapRenderer(PMVSData, PMFSData, EMT_PARALLAX_MAP_TRANSPARENT_VERTEX_ALPHA, this)); addAndDropMaterialRenderer(new COGLES2FixedPipelineRenderer(FPVSData, FPFSData, EMT_ONETEXTURE_BLEND, this)); + + delete[] FPVSData; + delete[] FPFSData; + delete[] NMVSData; + delete[] NMFSData; + delete[] PMVSData; + delete[] PMFSData; // Create 2D material renderer. @@ -476,6 +483,9 @@ namespace video R2DFSFile->drop(); MaterialRenderer2D = new COGLES2Renderer2D(R2DVSData, R2DFSData, this); + + delete[] R2DVSData; + delete[] R2DFSData; } From 0a2d05a998ff354d7d0e07e86cd632e206424299 Mon Sep 17 00:00:00 2001 From: Deve Date: Tue, 15 Aug 2017 09:00:49 +0200 Subject: [PATCH 042/125] Add a possibility to convert png->jpg for spm format for smaller apk --- android/generate_assets.sh | 144 +++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) diff --git a/android/generate_assets.sh b/android/generate_assets.sh index b76822c6a..2c0c26d47 100755 --- a/android/generate_assets.sh +++ b/android/generate_assets.sh @@ -401,6 +401,149 @@ convert_b3d() mv tmp.b3d "$FILE" } +convert_spm() +{ + if [ -z "$1" ]; then + echo "No file to convert" + return + fi + + FILE="$1" + echo "Convert file: $FILE" + + if [ ! -f "$FILE" ]; then + echo " File doesn't exist." + return + fi + + HEX_FILE=`hexdump -ve '1/1 "%.2x"' "$FILE"` + + SP_HEADER="5350" + SP_FOUND=`echo $HEX_FILE | head -c 4` + + if [ -z "$SP_FOUND" ] || [ "$SP_FOUND" != "$SP_HEADER" ]; then + echo " Unsupported format." + return + fi + + TEXS_BEGIN=60 + TEXS_COUNT=`echo $HEX_FILE | head -c $TEXS_BEGIN | tail -c 4` + + TEXS_COUNT_CONVERTED=`echo $TEXS_COUNT | cut -c3-4` + TEXS_COUNT_CONVERTED=$TEXS_COUNT_CONVERTED`echo $TEXS_COUNT | cut -c1-2` + TEXS_COUNT_CONVERTED=`echo $((0x$TEXS_COUNT_CONVERTED))` + TEXS_COUNT_CONVERTED=$(($TEXS_COUNT_CONVERTED * 2)) + + if [ $TEXS_COUNT_CONVERTED -le 0 ]; then + echo " Invalid textures count value." + return + fi + + CURR_POS=$(($TEXS_BEGIN + 2)) + + cp "$FILE" tmp.spm + + while [ $TEXS_COUNT_CONVERTED -gt 0 ]; do + TEXS_COUNT_CONVERTED=$(($TEXS_COUNT_CONVERTED - 1)) + + TEX_LEN=`echo $HEX_FILE | head -c $(($CURR_POS)) | tail -c 2` + TEX_LEN=`echo $((0x$TEX_LEN))` + + TEXNAME_BEGIN=$(($CURR_POS / 2)) + TEXNAME_END=$(($CURR_POS / 2 + $TEX_LEN)) + CURR_POS=$(($CURR_POS + 2 + $TEX_LEN * 2)) + + if [ $TEX_LEN -eq 0 ]; then + echo " Empty texture name, ignore..." + continue + fi + + TEXTURE_NAME=`dd if="$FILE" bs=1 skip=$TEXNAME_BEGIN \ + count=$(($TEXNAME_END - $TEXNAME_BEGIN)) 2> /dev/null` + + DIRNAME=`dirname "$FILE"` + TEXTURE_PATH="$DIRNAME/$TEXTURE_NAME" + + echo " Texture: $TEXTURE_NAME" + + ALREADY_CONVERTED=0 + + if [ -s "./converted_textures" ]; then + while read -r CONVERTED_TEXTURE; do + if [ "$TEXTURE_PATH" = "$CONVERTED_TEXTURE" ]; then + ALREADY_CONVERTED=1 + break + fi + done < "./converted_textures" + fi + + if [ $ALREADY_CONVERTED -eq 0 ]; then + if [ ! -f "$TEXTURE_PATH" ]; then + echo " Couldn't find texture file. Ignore..." + continue + fi + + FILE_EXTENSION=`echo "$TEXTURE_PATH" | tail -c 5` + + if [ `echo "$FILE_EXTENSION" | head -c 1` != "." ]; then + echo " Unsupported file extension. Ignore..." + continue + fi + + FILE_FORMAT=`identify -format %m "$TEXTURE_PATH"` + + if [ "$FILE_FORMAT" = "JPEG" ]; then + echo " File is already JPEG. Ignore..." + continue + fi + + #IS_OPAQUE=`identify -format '%[opaque]' "$TEXTURE_PATH"` + HAS_ALPHA=`identify -format '%A' "$TEXTURE_PATH"` + + if [ "$HAS_ALPHA" = "True" ] || [ "$HAS_ALPHA" = "true" ]; then + echo " File has alpha channel. Ignore..." + continue + fi + + NEW_TEXTURE_NAME="`echo $TEXTURE_NAME | head -c -5`.jpg" + NEW_TEXTURE_PATH="`echo $TEXTURE_PATH | head -c -5`.jpg" + + if [ -f "$NEW_TEXTURE_PATH" ]; then + echo " There is already a file with .jpg extension. Ignore..." + continue + fi + + convert -quality $JPEG_QUALITY "$TEXTURE_PATH" "$NEW_TEXTURE_PATH" + rm -f "$TEXTURE_PATH" + + if [ -s "$DIRNAME/materials.xml" ]; then + sed -i "s/name=\"$TEXTURE_NAME\"/name=\"$NEW_TEXTURE_NAME\"/g" \ + "$DIRNAME/materials.xml" + fi + + if [ -s "$DIRNAME/scene.xml" ]; then + sed -i "s/name=\"$TEXTURE_NAME\"/name=\"$NEW_TEXTURE_NAME\"/g" \ + "$DIRNAME/scene.xml" + fi + + echo "$TEXTURE_PATH" >> "./converted_textures" + fi + + echo -n ".jpg" | dd of=./tmp.spm bs=1 seek=$(($TEXNAME_END - 4)) \ + conv=notrunc 2> /dev/null + done + + SIZE_OLD=`du -b "$FILE" | cut -f1` + SIZE_NEW=`du -b "tmp.spm" | cut -f1` + + if [ $SIZE_NEW -ne $SIZE_OLD ]; then + echo " Something went wrong..." + exit + fi + + mv tmp.spm "$FILE" +} + if [ $DECREASE_QUALITY -gt 0 ]; then find assets/data -iname "*.png" | while read f; do convert_image "$f" "png"; done @@ -411,6 +554,7 @@ fi if [ $CONVERT_TO_JPG -gt 0 ]; then find assets/data -iname "*.b3d" | while read f; do convert_b3d "$f"; done + find assets/data -iname "*.spm" | while read f; do convert_spm "$f"; done if [ -s "./converted_textures" ]; then echo "Converted textures:" From 4da945f710480fd1a0fc10d50dd741715c873809 Mon Sep 17 00:00:00 2001 From: Deve Date: Tue, 15 Aug 2017 09:58:06 +0200 Subject: [PATCH 043/125] Move duplicated code to separate function --- android/generate_assets.sh | 250 ++++++++++++++----------------------- 1 file changed, 94 insertions(+), 156 deletions(-) diff --git a/android/generate_assets.sh b/android/generate_assets.sh index 2c0c26d47..c2774b113 100755 --- a/android/generate_assets.sh +++ b/android/generate_assets.sh @@ -250,6 +250,92 @@ convert_sound() rm -f tmp.wav tmp.ogg } +convert_model() +{ + if [ -z "$1" ]; then + echo "No texture path provided" + return + fi + + TEXTURE_PATH="$1" + + echo " Texture: $TEXTURE_NAME" + + ALREADY_CONVERTED=0 + + if [ -s "./converted_textures" ]; then + while read -r CONVERTED_TEXTURE; do + if [ "$TEXTURE_PATH" = "$CONVERTED_TEXTURE" ]; then + ALREADY_CONVERTED=1 + break + fi + done < "./converted_textures" + fi + + if [ $ALREADY_CONVERTED -eq 0 ]; then + if [ ! -f "$TEXTURE_PATH" ]; then + echo " Couldn't find texture file. Ignore..." + continue + fi + + FILE_EXTENSION=`echo "$TEXTURE_PATH" | tail -c 5` + + if [ `echo "$FILE_EXTENSION" | head -c 1` != "." ]; then + echo " Unsupported file extension. Ignore..." + continue + fi + + FILE_FORMAT=`identify -format %m "$TEXTURE_PATH"` + + if [ "$FILE_FORMAT" = "JPEG" ]; then + echo " File is already JPEG. Ignore..." + continue + fi + + #IS_OPAQUE=`identify -format '%[opaque]' "$TEXTURE_PATH"` + HAS_ALPHA=`identify -format '%A' "$TEXTURE_PATH"` + + if [ "$HAS_ALPHA" = "True" ] || [ "$HAS_ALPHA" = "true" ]; then + echo " File has alpha channel. Ignore..." + continue + fi + + NEW_TEXTURE_NAME="`echo $TEXTURE_NAME | head -c -5`.jpg" + NEW_TEXTURE_PATH="`echo $TEXTURE_PATH | head -c -5`.jpg" + + if [ -f "$NEW_TEXTURE_PATH" ]; then + echo " There is already a file with .jpg extension. Ignore..." + continue + fi + + convert -quality $JPEG_QUALITY "$TEXTURE_PATH" "$NEW_TEXTURE_PATH" + rm -f "$TEXTURE_PATH" + + if [ -s "$DIRNAME/materials.xml" ]; then + sed -i "s/name=\"$TEXTURE_NAME\"/name=\"$NEW_TEXTURE_NAME\"/g" \ + "$DIRNAME/materials.xml" + fi + + if [ -s "$DIRNAME/scene.xml" ]; then + sed -i "s/name=\"$TEXTURE_NAME\"/name=\"$NEW_TEXTURE_NAME\"/g" \ + "$DIRNAME/scene.xml" + fi + + echo "$TEXTURE_PATH" >> "./converted_textures" + fi + + echo -n ".jpg" | dd of=./tmp bs=1 seek=$(($TEXNAME_END - 4)) \ + conv=notrunc 2> /dev/null + + SIZE_OLD=`du -b "$FILE" | cut -f1` + SIZE_NEW=`du -b "tmp" | cut -f1` + + if [ $SIZE_NEW -ne $SIZE_OLD ]; then + echo " Something went wrong..." + exit + fi +} + convert_b3d() { if [ -z "$1" ]; then @@ -296,7 +382,7 @@ convert_b3d() HEX_TEXS=`echo $HEX_FILE | head -c $TEXS_END | tail -c +$(($TEXS_BEGIN+1))` CURR_POS=0 - cp "$FILE" tmp.b3d + cp "$FILE" tmp while [ $CURR_POS -lt $TEXS_END ]; do NULL_POS=`echo $HEX_TEXS | tail -c +$(($CURR_POS+1)) | grep -b -o "00" \ @@ -320,85 +406,11 @@ convert_b3d() count=$(($TEXNAME_END - $TEXNAME_BEGIN)) 2> /dev/null` DIRNAME=`dirname "$FILE"` TEXTURE_PATH="$DIRNAME/$TEXTURE_NAME" - - echo " Texture: $TEXTURE_NAME" - - ALREADY_CONVERTED=0 - - if [ -s "./converted_textures" ]; then - while read -r CONVERTED_TEXTURE; do - if [ "$TEXTURE_PATH" = "$CONVERTED_TEXTURE" ]; then - ALREADY_CONVERTED=1 - break - fi - done < "./converted_textures" - fi - - if [ $ALREADY_CONVERTED -eq 0 ]; then - if [ ! -f "$TEXTURE_PATH" ]; then - echo " Couldn't find texture file. Ignore..." - continue - fi - - FILE_EXTENSION=`echo "$TEXTURE_PATH" | tail -c 5` - - if [ `echo "$FILE_EXTENSION" | head -c 1` != "." ]; then - echo " Unsupported file extension. Ignore..." - continue - fi - - FILE_FORMAT=`identify -format %m "$TEXTURE_PATH"` - - if [ "$FILE_FORMAT" = "JPEG" ]; then - echo " File is already JPEG. Ignore..." - continue - fi - - #IS_OPAQUE=`identify -format '%[opaque]' "$TEXTURE_PATH"` - HAS_ALPHA=`identify -format '%A' "$TEXTURE_PATH"` - - if [ "$HAS_ALPHA" = "True" ] || [ "$HAS_ALPHA" = "true" ]; then - echo " File has alpha channel. Ignore..." - continue - fi - - NEW_TEXTURE_NAME="`echo $TEXTURE_NAME | head -c -5`.jpg" - NEW_TEXTURE_PATH="`echo $TEXTURE_PATH | head -c -5`.jpg" - - if [ -f "$NEW_TEXTURE_PATH" ]; then - echo " There is already a file with .jpg extension. Ignore..." - continue - fi - - convert -quality $JPEG_QUALITY "$TEXTURE_PATH" "$NEW_TEXTURE_PATH" - rm -f "$TEXTURE_PATH" - - if [ -s "$DIRNAME/materials.xml" ]; then - sed -i "s/name=\"$TEXTURE_NAME\"/name=\"$NEW_TEXTURE_NAME\"/g" \ - "$DIRNAME/materials.xml" - fi - - if [ -s "$DIRNAME/scene.xml" ]; then - sed -i "s/name=\"$TEXTURE_NAME\"/name=\"$NEW_TEXTURE_NAME\"/g" \ - "$DIRNAME/scene.xml" - fi - - echo "$TEXTURE_PATH" >> "./converted_textures" - fi - - echo -n ".jpg" | dd of=./tmp.b3d bs=1 seek=$(($TEXNAME_END - 4)) \ - conv=notrunc 2> /dev/null + + convert_model "$TEXTURE_PATH" done - SIZE_OLD=`du -b "$FILE" | cut -f1` - SIZE_NEW=`du -b "tmp.b3d" | cut -f1` - - if [ $SIZE_NEW -ne $SIZE_OLD ]; then - echo " Something went wrong..." - exit - fi - - mv tmp.b3d "$FILE" + mv tmp "$FILE" } convert_spm() @@ -441,7 +453,7 @@ convert_spm() CURR_POS=$(($TEXS_BEGIN + 2)) - cp "$FILE" tmp.spm + cp "$FILE" tmp while [ $TEXS_COUNT_CONVERTED -gt 0 ]; do TEXS_COUNT_CONVERTED=$(($TEXS_COUNT_CONVERTED - 1)) @@ -463,85 +475,11 @@ convert_spm() DIRNAME=`dirname "$FILE"` TEXTURE_PATH="$DIRNAME/$TEXTURE_NAME" - - echo " Texture: $TEXTURE_NAME" - - ALREADY_CONVERTED=0 - - if [ -s "./converted_textures" ]; then - while read -r CONVERTED_TEXTURE; do - if [ "$TEXTURE_PATH" = "$CONVERTED_TEXTURE" ]; then - ALREADY_CONVERTED=1 - break - fi - done < "./converted_textures" - fi - - if [ $ALREADY_CONVERTED -eq 0 ]; then - if [ ! -f "$TEXTURE_PATH" ]; then - echo " Couldn't find texture file. Ignore..." - continue - fi - - FILE_EXTENSION=`echo "$TEXTURE_PATH" | tail -c 5` - - if [ `echo "$FILE_EXTENSION" | head -c 1` != "." ]; then - echo " Unsupported file extension. Ignore..." - continue - fi - - FILE_FORMAT=`identify -format %m "$TEXTURE_PATH"` - - if [ "$FILE_FORMAT" = "JPEG" ]; then - echo " File is already JPEG. Ignore..." - continue - fi - - #IS_OPAQUE=`identify -format '%[opaque]' "$TEXTURE_PATH"` - HAS_ALPHA=`identify -format '%A' "$TEXTURE_PATH"` - - if [ "$HAS_ALPHA" = "True" ] || [ "$HAS_ALPHA" = "true" ]; then - echo " File has alpha channel. Ignore..." - continue - fi - - NEW_TEXTURE_NAME="`echo $TEXTURE_NAME | head -c -5`.jpg" - NEW_TEXTURE_PATH="`echo $TEXTURE_PATH | head -c -5`.jpg" - - if [ -f "$NEW_TEXTURE_PATH" ]; then - echo " There is already a file with .jpg extension. Ignore..." - continue - fi - - convert -quality $JPEG_QUALITY "$TEXTURE_PATH" "$NEW_TEXTURE_PATH" - rm -f "$TEXTURE_PATH" - - if [ -s "$DIRNAME/materials.xml" ]; then - sed -i "s/name=\"$TEXTURE_NAME\"/name=\"$NEW_TEXTURE_NAME\"/g" \ - "$DIRNAME/materials.xml" - fi - - if [ -s "$DIRNAME/scene.xml" ]; then - sed -i "s/name=\"$TEXTURE_NAME\"/name=\"$NEW_TEXTURE_NAME\"/g" \ - "$DIRNAME/scene.xml" - fi - - echo "$TEXTURE_PATH" >> "./converted_textures" - fi - - echo -n ".jpg" | dd of=./tmp.spm bs=1 seek=$(($TEXNAME_END - 4)) \ - conv=notrunc 2> /dev/null + + convert_model "$TEXTURE_PATH" done - - SIZE_OLD=`du -b "$FILE" | cut -f1` - SIZE_NEW=`du -b "tmp.spm" | cut -f1` - if [ $SIZE_NEW -ne $SIZE_OLD ]; then - echo " Something went wrong..." - exit - fi - - mv tmp.spm "$FILE" + mv tmp "$FILE" } From 8fa0e4e7b356b95fedcf74cae41fd64e15a3d45b Mon Sep 17 00:00:00 2001 From: Deve Date: Tue, 15 Aug 2017 10:10:17 +0200 Subject: [PATCH 044/125] Make it less verbose --- android/generate_assets.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/android/generate_assets.sh b/android/generate_assets.sh index c2774b113..180aa2b93 100755 --- a/android/generate_assets.sh +++ b/android/generate_assets.sh @@ -259,7 +259,7 @@ convert_model() TEXTURE_PATH="$1" - echo " Texture: $TEXTURE_NAME" + #echo " Texture: $TEXTURE_NAME" ALREADY_CONVERTED=0 @@ -274,21 +274,21 @@ convert_model() if [ $ALREADY_CONVERTED -eq 0 ]; then if [ ! -f "$TEXTURE_PATH" ]; then - echo " Couldn't find texture file. Ignore..." + #echo " Couldn't find texture file. Ignore..." continue fi FILE_EXTENSION=`echo "$TEXTURE_PATH" | tail -c 5` if [ `echo "$FILE_EXTENSION" | head -c 1` != "." ]; then - echo " Unsupported file extension. Ignore..." + #echo " Unsupported file extension. Ignore..." continue fi FILE_FORMAT=`identify -format %m "$TEXTURE_PATH"` if [ "$FILE_FORMAT" = "JPEG" ]; then - echo " File is already JPEG. Ignore..." + #echo " File is already JPEG. Ignore..." continue fi @@ -296,7 +296,7 @@ convert_model() HAS_ALPHA=`identify -format '%A' "$TEXTURE_PATH"` if [ "$HAS_ALPHA" = "True" ] || [ "$HAS_ALPHA" = "true" ]; then - echo " File has alpha channel. Ignore..." + #echo " File has alpha channel. Ignore..." continue fi @@ -304,7 +304,7 @@ convert_model() NEW_TEXTURE_PATH="`echo $TEXTURE_PATH | head -c -5`.jpg" if [ -f "$NEW_TEXTURE_PATH" ]; then - echo " There is already a file with .jpg extension. Ignore..." + #echo " There is already a file with .jpg extension. Ignore..." continue fi @@ -389,7 +389,7 @@ convert_b3d() | head -n 1 | cut -f1 -d":"` if [ -z $NULL_POS ]; then - echo " Done." + #echo " Done." break fi @@ -466,7 +466,7 @@ convert_spm() CURR_POS=$(($CURR_POS + 2 + $TEX_LEN * 2)) if [ $TEX_LEN -eq 0 ]; then - echo " Empty texture name, ignore..." + #echo " Empty texture name, ignore..." continue fi From 01be8002531256bdaaab6ba63bba27125762eebb Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 15 Aug 2017 23:54:39 +1000 Subject: [PATCH 045/125] Fixed stack traces for win32 (#2642). --- src/utils/crash_reporting.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/crash_reporting.cpp b/src/utils/crash_reporting.cpp index debe715e3..0440c9360 100644 --- a/src/utils/crash_reporting.cpp +++ b/src/utils/crash_reporting.cpp @@ -193,8 +193,8 @@ } // Only keep the directory - char* last_separator = strchr(filepath, '/'); - if(!last_separator) last_separator = strchr(filepath, '\\'); + char* last_separator = strrchr(filepath, '/'); + if(!last_separator) last_separator = strrchr(filepath, '\\'); if(last_separator) last_separator[0] = '\0'; From 196fe378b4c9c6455d2066d783a85d206c26ea81 Mon Sep 17 00:00:00 2001 From: Deve Date: Tue, 15 Aug 2017 22:42:53 +0200 Subject: [PATCH 046/125] Fix for png->jpg converter. It's possible that there are multiple file names in single parameter, for example: now it should work. Also use is_opaque rather than has_alpha that gives better results. --- android/generate_assets.sh | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/android/generate_assets.sh b/android/generate_assets.sh index 180aa2b93..aeae74e36 100755 --- a/android/generate_assets.sh +++ b/android/generate_assets.sh @@ -292,10 +292,10 @@ convert_model() continue fi - #IS_OPAQUE=`identify -format '%[opaque]' "$TEXTURE_PATH"` - HAS_ALPHA=`identify -format '%A' "$TEXTURE_PATH"` + IS_OPAQUE=`identify -format '%[opaque]' "$TEXTURE_PATH"` + #HAS_ALPHA=`identify -format '%A' "$TEXTURE_PATH"` - if [ "$HAS_ALPHA" = "True" ] || [ "$HAS_ALPHA" = "true" ]; then + if [ "$IS_OPAQUE" = "False" ] || [ "$IS_OPAQUE" = "false" ]; then #echo " File has alpha channel. Ignore..." continue fi @@ -312,13 +312,22 @@ convert_model() rm -f "$TEXTURE_PATH" if [ -s "$DIRNAME/materials.xml" ]; then - sed -i "s/name=\"$TEXTURE_NAME\"/name=\"$NEW_TEXTURE_NAME\"/g" \ - "$DIRNAME/materials.xml" + sed -i "s/\"$TEXTURE_NAME\"/\"$NEW_TEXTURE_NAME\"/g" \ + "$DIRNAME/materials.xml" + sed -i "s/\"$TEXTURE_NAME /\"$NEW_TEXTURE_NAME /g" \ + "$DIRNAME/materials.xml" + sed -i "s/ $TEXTURE_NAME\"/ $NEW_TEXTURE_NAME\"/g" \ + "$DIRNAME/materials.xml" + sed -i "s/ $TEXTURE_NAME / $NEW_TEXTURE_NAME /g" \ + "$DIRNAME/materials.xml" fi if [ -s "$DIRNAME/scene.xml" ]; then - sed -i "s/name=\"$TEXTURE_NAME\"/name=\"$NEW_TEXTURE_NAME\"/g" \ - "$DIRNAME/scene.xml" + sed -i "s/\"$TEXTURE_NAME\"/\"$NEW_TEXTURE_NAME\"/g" \ + "$DIRNAME/scene.xml" + sed -i "s/\"$TEXTURE_NAME /\"$NEW_TEXTURE_NAME /g" "$DIRNAME/scene.xml" + sed -i "s/ $TEXTURE_NAME\"/ $NEW_TEXTURE_NAME\"/g" "$DIRNAME/scene.xml" + sed -i "s/ $TEXTURE_NAME / $NEW_TEXTURE_NAME /g" "$DIRNAME/scene.xml" fi echo "$TEXTURE_PATH" >> "./converted_textures" From 3031fabcf930a3a386df32cca51e62a475aedf0f Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 16 Aug 2017 13:48:27 +0800 Subject: [PATCH 047/125] Add spm loader --- data/stk_config.xml | 4 +- lib/irrlicht/source/Irrlicht/CSkinnedMesh.cpp | 18 +- lib/irrlicht/source/Irrlicht/CSkinnedMesh.h | 3 + src/graphics/irr_driver.cpp | 4 + src/graphics/sp_mesh_loader.cpp | 637 ++++++++++++++++++ src/graphics/sp_mesh_loader.hpp | 403 +++++++++++ src/graphics/stk_mesh_loader.cpp | 1 + src/tracks/track.cpp | 55 +- 8 files changed, 1100 insertions(+), 25 deletions(-) create mode 100644 src/graphics/sp_mesh_loader.cpp create mode 100644 src/graphics/sp_mesh_loader.hpp diff --git a/data/stk_config.xml b/data/stk_config.xml index 4556f524a..1bc90808b 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -3,11 +3,11 @@ - + - + - - - - + + + + diff --git a/data/powerup.xml b/data/powerup.xml index 2c581f13a..5c92bfe0c 100644 --- a/data/powerup.xml +++ b/data/powerup.xml @@ -2,17 +2,17 @@ + model="anchor.spm" /> + model="parachute.spm" /> diff --git a/src/items/attachment_manager.cpp b/src/items/attachment_manager.cpp index 1238d36d7..b49bf4bb8 100644 --- a/src/items/attachment_manager.cpp +++ b/src/items/attachment_manager.cpp @@ -48,14 +48,14 @@ struct initAttachmentType {Attachment::AttachmentType attachment; static const initAttachmentType iat[]= { - {Attachment::ATTACH_PARACHUTE, "parachute.b3d", "parachute-attach-icon.png" }, - {Attachment::ATTACH_BOMB, "bomb.b3d", "bomb-attach-icon.png" }, - {Attachment::ATTACH_ANVIL, "anchor.b3d", "anchor-attach-icon.png" }, - {Attachment::ATTACH_SWATTER, "swatter.b3d", "swatter-icon.png" }, - {Attachment::ATTACH_NOLOKS_SWATTER, "swatter_nolok.b3d", "swatter-icon.png" }, - {Attachment::ATTACH_TINYTUX, "reset-button.b3d", "reset-attach-icon.png" }, - {Attachment::ATTACH_BUBBLEGUM_SHIELD, "bubblegum_shield.b3d", "shield-icon.png" }, - {Attachment::ATTACH_NOLOK_BUBBLEGUM_SHIELD, "bubblegum_shield_nolok.b3d", "shield-icon.png" }, + {Attachment::ATTACH_PARACHUTE, "parachute.spm", "parachute-attach-icon.png" }, + {Attachment::ATTACH_BOMB, "bomb.spm", "bomb-attach-icon.png" }, + {Attachment::ATTACH_ANVIL, "anchor.spm", "anchor-attach-icon.png" }, + {Attachment::ATTACH_SWATTER, "swatter.spm", "swatter-icon.png" }, + {Attachment::ATTACH_NOLOKS_SWATTER, "swatter_nolok.spm", "swatter-icon.png" }, + {Attachment::ATTACH_TINYTUX, "reset-button.spm", "reset-attach-icon.png" }, + {Attachment::ATTACH_BUBBLEGUM_SHIELD, "bubblegum_shield.spm", "shield-icon.png" }, + {Attachment::ATTACH_NOLOK_BUBBLEGUM_SHIELD, "bubblegum_shield_nolok.spm", "shield-icon.png" }, {Attachment::ATTACH_MAX, "", "" }, }; diff --git a/src/items/swatter.cpp b/src/items/swatter.cpp index 41803c972..4241d6654 100644 --- a/src/items/swatter.cpp +++ b/src/items/swatter.cpp @@ -75,7 +75,7 @@ Swatter::Swatter(AbstractKart *kart, bool was_bomb, if (m_removing_bomb) { m_scene_node->setMesh(irr_driver->getAnimatedMesh( - file_manager->getAsset(FileManager::MODEL,"swatter_anim2.b3d") ) ); + file_manager->getAsset(FileManager::MODEL,"swatter_anim.spm") ) ); m_scene_node->setRotation(core::vector3df(0.0, -180.0, 0.0)); m_scene_node->setAnimationSpeed(0.9f); m_scene_node->setCurrentFrame(0.0f); diff --git a/src/karts/kart_model.cpp b/src/karts/kart_model.cpp index 90b53ba36..f3e2313f8 100644 --- a/src/karts/kart_model.cpp +++ b/src/karts/kart_model.cpp @@ -770,7 +770,7 @@ void KartModel::loadHeadlights(const XMLNode &node) const XMLNode* child = node.getNode(i); if (child->getName() == "object") { - // + // core::vector3df position; child->get("position", &position); diff --git a/src/main.cpp b/src/main.cpp index 05df09787..719da100c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -421,7 +421,7 @@ void handleXmasMode() } // switch m_xmas_mode if(xmas) - kart_properties_manager->setHatMeshName("christmas_hat.b3d"); + kart_properties_manager->setHatMeshName("christmas_hat.spm"); } // handleXmasMode // ============================================================================ /** Determines if Easter Ears should be used @@ -478,7 +478,7 @@ void handleEasterEarMode() int day, month, year; StkTime::getDate(&day, &month, &year); if (isEasterMode(day, month, year, /*before_after_days*/5)) - kart_properties_manager->setHatMeshName("easter_ears.b3d"); + kart_properties_manager->setHatMeshName("easter_ears.spm"); } // handleEasterMode // ============================================================================ diff --git a/src/modes/three_strikes_battle.cpp b/src/modes/three_strikes_battle.cpp index 86f789243..a6231ff35 100644 --- a/src/modes/three_strikes_battle.cpp +++ b/src/modes/three_strikes_battle.cpp @@ -51,7 +51,7 @@ ThreeStrikesBattle::ThreeStrikesBattle() : WorldWithRank() m_insert_tire = 0; m_tire = irr_driver->getMesh(file_manager->getAsset(FileManager::MODEL, - "tire.b3d") ); + "tire.spm") ); irr_driver->grabAllTextures(m_tire); m_total_rescue = 0; @@ -375,7 +375,7 @@ void ThreeStrikesBattle::update(float dt) if(m_insert_tire == 1) { tire_offset = core::vector3df(0.0f, 0.0f, 0.0f); - tire = file_manager->getAsset(FileManager::MODEL,"tire.b3d"); + tire = file_manager->getAsset(FileManager::MODEL,"tire.spm"); scale = 0.5f; radius = 0.5f; body_shape = PhysicalObject::MP_CYLINDER_Y; @@ -387,13 +387,13 @@ void ThreeStrikesBattle::update(float dt) radius = m_tire_radius[m_insert_tire-2]; tire_offset = m_tire_offsets[m_insert_tire-2]; if (m_insert_tire == 2) - tire = m_tire_dir+"/wheel-rear-left.b3d"; + tire = m_tire_dir+"/wheel-rear-left.spm"; else if(m_insert_tire == 3) - tire = m_tire_dir+"/wheel-front-left.b3d"; + tire = m_tire_dir+"/wheel-front-left.spm"; else if(m_insert_tire == 4) - tire = m_tire_dir+"/wheel-front-right.b3d"; + tire = m_tire_dir+"/wheel-front-right.spm"; else if(m_insert_tire == 5) - tire = m_tire_dir+"/wheel-rear-right.b3d"; + tire = m_tire_dir+"/wheel-rear-right.spm"; if(!file_manager->fileExists(tire)) { m_insert_tire--; diff --git a/src/states_screens/feature_unlocked.cpp b/src/states_screens/feature_unlocked.cpp index a0d728ed2..f4d8b4e19 100644 --- a/src/states_screens/feature_unlocked.cpp +++ b/src/states_screens/feature_unlocked.cpp @@ -216,13 +216,13 @@ void FeatureUnlockedCutScene::addTrophy(RaceManager::Difficulty difficulty) switch (difficulty) { case RaceManager::DIFFICULTY_EASY: - model = file_manager->getAsset(FileManager::MODEL,"trophy_bronze.b3d"); + model = file_manager->getAsset(FileManager::MODEL,"trophy_bronze.spm"); break; case RaceManager::DIFFICULTY_MEDIUM: - model = file_manager->getAsset(FileManager::MODEL,"trophy_silver.b3d"); + model = file_manager->getAsset(FileManager::MODEL,"trophy_silver.spm"); break; case RaceManager::DIFFICULTY_HARD: - model = file_manager->getAsset(FileManager::MODEL,"trophy_gold.b3d"); + model = file_manager->getAsset(FileManager::MODEL,"trophy_gold.spm"); break; default: assert(false); diff --git a/src/states_screens/grand_prix_win.cpp b/src/states_screens/grand_prix_win.cpp index d992b6931..30e9bca59 100644 --- a/src/states_screens/grand_prix_win.cpp +++ b/src/states_screens/grand_prix_win.cpp @@ -376,11 +376,11 @@ void GrandPrixWin::setKarts(const std::string idents_arg[3]) TrackObjectPresentationMesh* meshPresentation = currObj->getPresentation(); if (meshPresentation != NULL) { - if (meshPresentation->getModelFile() == "gpwin_podium1.b3d") + if (meshPresentation->getModelFile() == "gpwin_podium1.spm") m_podium_steps[0] = currObj; - else if (meshPresentation->getModelFile() == "gpwin_podium2.b3d") + else if (meshPresentation->getModelFile() == "gpwin_podium2.spm") m_podium_steps[1] = currObj; - else if (meshPresentation->getModelFile() == "gpwin_podium3.b3d") + else if (meshPresentation->getModelFile() == "gpwin_podium3.spm") m_podium_steps[2] = currObj; } } diff --git a/src/states_screens/kart_selection.cpp b/src/states_screens/kart_selection.cpp index db7cc3d76..3afc5663e 100644 --- a/src/states_screens/kart_selection.cpp +++ b/src/states_screens/kart_selection.cpp @@ -844,7 +844,7 @@ void KartSelectionScreen::updateKartWidgetModel(int widget_id, { w3->clearModels(); w3->addModel(irr_driver->getAnimatedMesh( - file_manager->getAsset(FileManager::MODEL, "chest.b3d") )->getMesh(20), + file_manager->getAsset(FileManager::MODEL, "chest.spm") )->getMesh(20), Vec3(0,0,0), Vec3(15.0f, 15.0f, 15.0f) ); w3->update(0); From bd84929982d9708861af09a62cf5cdd7e1555de6 Mon Sep 17 00:00:00 2001 From: Deve Date: Mon, 21 Aug 2017 11:27:14 +0200 Subject: [PATCH 057/125] Don't convert gloss maps. It causes artifacts when advanced lighting is enabled. --- android/generate_assets.sh | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/android/generate_assets.sh b/android/generate_assets.sh index d0f2f32ec..d3b61a5c4 100755 --- a/android/generate_assets.sh +++ b/android/generate_assets.sh @@ -19,7 +19,7 @@ ################################################################################ -export KARTS="elephpant gnu nolok pidgin suzanne tux xue" +export KARTS="all" export TRACKS="battleisland cornfield_crossing featunlocked gplose gpwin \ hacienda introcutscene introcutscene2 lighthouse olivermath \ overworld sandtrack scotland snowmountain snowtuxpeak \ @@ -42,6 +42,8 @@ export RUN_OPTIMIZE_SCRIPT=0 export DECREASE_QUALITY=1 export CONVERT_TO_JPG=1 +export CONVERT_TO_JPG_BLACKLIST="data/karts/hexley/hexley_kart_diffuse.png" + export BLACKLIST_FILES="data/music/cocoa_river_fast.ogg2" ################################################################################ @@ -276,9 +278,18 @@ convert_to_jpg() if [ $ALREADY_CONVERTED -eq 1 ]; then return fi - - if [ ! -f "$FILE" ]; then - #echo " Couldn't find texture file. Ignore..." + + BLACKLISTED=0 + + for BLACKLIST_FILE in $CONVERT_TO_JPG_BLACKLIST; do + if [ "$FILE" = "assets/$BLACKLIST_FILE" ]; then + BLACKLISTED=1 + break + fi + done + + if [ $BLACKLISTED -eq 1 ]; then + #echo " File is blacklisted. Ignore..." continue fi @@ -304,6 +315,16 @@ convert_to_jpg() continue fi + DIRNAME="`dirname "$FILE"`" + BASENAME="`basename "$FILE"`" + IS_GLOSS_MAP=`find "$DIRNAME" -iname "*.xml" -exec cat {} \; \ + | grep -c "gloss-map=\"$BASENAME\""` + + if [ $IS_GLOSS_MAP -gt 0 ]; then + #echo " File is a gloss-map. Ignore..." + continue + fi + NEW_FILE="`echo $FILE | head -c -5`.jpg" if [ -f "$NEW_FILE" ]; then @@ -526,6 +547,8 @@ fi if [ $CONVERT_TO_JPG -gt 0 ]; then + rm -f "./converted_textures" + find assets/data -not -path "assets/data/textures/*" -iname "*.png" | while read f; do convert_to_jpg "$f"; done find assets/data -iname "*.b3dz" | while read f; do convert_to_jpg_extract_b3dz "$f"; done From 6a03f147f6194a9329e5ae8ad9a0a55271f6660c Mon Sep 17 00:00:00 2001 From: Deve Date: Tue, 22 Aug 2017 00:02:34 +0200 Subject: [PATCH 058/125] Add a possibility to use ubo on gles. Disabled by default on android, because it's been tested on only one device and it's a bit slower in most cases. --- data/graphical_restrictions.xml | 1 + src/graphics/central_settings.cpp | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/data/graphical_restrictions.xml b/data/graphical_restrictions.xml index f9db1ec2f..999b5520a 100644 --- a/data/graphical_restrictions.xml +++ b/data/graphical_restrictions.xml @@ -39,4 +39,5 @@ + diff --git a/src/graphics/central_settings.cpp b/src/graphics/central_settings.cpp index f6e8127cc..a7925643a 100644 --- a/src/graphics/central_settings.cpp +++ b/src/graphics/central_settings.cpp @@ -252,6 +252,13 @@ void CentralVideoSettings::init() Log::info("GLDriver", "Explicit Attrib Location Present"); hasExplicitAttribLocation = true; } + + if (!GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_UNIFORM_BUFFER_OBJECT) && + m_glsl == true) + { + hasUBO = true; + Log::info("GLDriver", "ARB Uniform Buffer Object Present"); + } if (!GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_TEXTURE_FORMAT_BGRA8888) && (hasGLExtension("GL_IMG_texture_format_BGRA8888") || From 850afc862073baff40a86cd13671b5bce4e7c68c Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 23 Aug 2017 00:55:25 +0800 Subject: [PATCH 059/125] Allow using animated texture with displace shader Plus some clean-up --- data/shaders/displace.frag | 1 - data/shaders/displace.vert | 22 ---------------------- data/shaders/object_pass.vert | 3 ++- src/graphics/geometry_passes.cpp | 1 + src/graphics/materials.cpp | 6 +++--- src/graphics/materials.hpp | 3 ++- 6 files changed, 8 insertions(+), 28 deletions(-) delete mode 100644 data/shaders/displace.vert diff --git a/data/shaders/displace.frag b/data/shaders/displace.frag index 748909f82..d76be70a6 100644 --- a/data/shaders/displace.frag +++ b/data/shaders/displace.frag @@ -6,7 +6,6 @@ uniform vec2 dir; uniform vec2 dir2; in vec2 uv; -in vec2 uv_bis; in float camdist; out vec4 FragColor; diff --git a/data/shaders/displace.vert b/data/shaders/displace.vert deleted file mode 100644 index e5acc43b5..000000000 --- a/data/shaders/displace.vert +++ /dev/null @@ -1,22 +0,0 @@ -uniform mat4 ModelMatrix; - -#ifdef Explicit_Attrib_Location_Usable -layout(location = 0) in vec3 Position; -layout(location = 3) in vec2 Texcoord; -layout(location = 4) in vec2 SecondTexcoord; -#else -in vec3 Position; -in vec2 Texcoord; -in vec2 SecondTexcoord; -#endif - -out vec2 uv; -out vec2 uv_bis; -out float camdist; - -void main() { - gl_Position = ProjectionViewMatrix * ModelMatrix * vec4(Position, 1.); - uv = Texcoord; - uv_bis = SecondTexcoord; - camdist = length(ViewMatrix * ModelMatrix * vec4(Position, 1.)); -} diff --git a/data/shaders/object_pass.vert b/data/shaders/object_pass.vert index 124e8c26e..ed9b2838e 100644 --- a/data/shaders/object_pass.vert +++ b/data/shaders/object_pass.vert @@ -41,7 +41,7 @@ out vec3 bitangent; out vec2 uv; out vec2 uv_bis; out vec4 color; - +out float camdist; void main(void) { @@ -56,4 +56,5 @@ void main(void) bitangent = (ViewMatrix * ModelMatrix * vec4(Bitangent, 0.)).xyz; uv = vec2(Texcoord.x + texture_trans.x, Texcoord.y + texture_trans.y); uv_bis = SecondTexcoord; + camdist = length(ViewMatrix * ModelMatrix * vec4(Position, 1.)); } diff --git a/src/graphics/geometry_passes.cpp b/src/graphics/geometry_passes.cpp index ae807470b..7100e4319 100644 --- a/src/graphics/geometry_passes.cpp +++ b/src/graphics/geometry_passes.cpp @@ -384,6 +384,7 @@ void AbstractGeometryPasses::renderTransparent(const DrawCalls& draw_calls, mesh.textures[0]->getOpenGLTextureName()); DisplaceShader::getInstance()->use(); DisplaceShader::getInstance()->setUniforms(AbsoluteTransformation, + mesh.texture_trans, core::vector2df(cb->getDirX(), cb->getDirY()), core::vector2df(cb->getDir2X(), cb->getDir2Y())); diff --git a/src/graphics/materials.cpp b/src/graphics/materials.cpp index 681f43b76..e8ed79880 100644 --- a/src/graphics/materials.cpp +++ b/src/graphics/materials.cpp @@ -329,7 +329,7 @@ InstancedRefShadowShader::InstancedRefShadowShader() // ============================================================================ DisplaceMaskShader::DisplaceMaskShader() { - loadProgram(OBJECT, GL_VERTEX_SHADER, "displace.vert", + loadProgram(OBJECT, GL_VERTEX_SHADER, "object_pass.vert", GL_FRAGMENT_SHADER, "white.frag"); assignUniforms("ModelMatrix"); } // DisplaceMaskShader @@ -337,9 +337,9 @@ DisplaceMaskShader::DisplaceMaskShader() // ============================================================================ DisplaceShader::DisplaceShader() { - loadProgram(OBJECT, GL_VERTEX_SHADER, "displace.vert", + loadProgram(OBJECT, GL_VERTEX_SHADER, "object_pass.vert", GL_FRAGMENT_SHADER, "displace.frag"); - assignUniforms("ModelMatrix", "dir", "dir2"); + assignUniforms("ModelMatrix", "texture_trans", "dir", "dir2"); assignSamplerNames(0, "displacement_tex", ST_BILINEAR_FILTERED, 1, "color_tex", ST_BILINEAR_FILTERED, 2, "mask_tex", ST_BILINEAR_FILTERED, diff --git a/src/graphics/materials.hpp b/src/graphics/materials.hpp index 8cce5a087..61ce7f847 100644 --- a/src/graphics/materials.hpp +++ b/src/graphics/materials.hpp @@ -212,7 +212,8 @@ public: // ============================================================================ class DisplaceShader : public TextureShader + core::vector2df, core::vector2df, + core::vector2df> { public: DisplaceShader(); From b03e10890222c23acf32b0b912f8da35743c5c4f Mon Sep 17 00:00:00 2001 From: Deve Date: Tue, 22 Aug 2017 23:52:02 +0200 Subject: [PATCH 060/125] Handle a case when request for srgb-capable framebuffer failed. It fixes #2902. --- .../source/Irrlicht/CIrrDeviceLinux.cpp | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp index 4f4ed1358..e253cecb6 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp @@ -719,6 +719,39 @@ bool CIrrDeviceLinux::createWindow() } } } + // Try to disable sRGB framebuffer + if (!configList && CreationParams.HandleSRGB) + { + os::Printer::log("No sRGB framebuffer available.", ELL_WARNING); + CreationParams.HandleSRGB=false; + visualAttrBuffer[21] = GLX_DONT_CARE; + configList=glxChooseFBConfig(display, screennr, visualAttrBuffer,&nitems); + if (!configList && CreationParams.AntiAlias) + { + while (!configList && (visualAttrBuffer[19]>1)) + { + visualAttrBuffer[19] -= 1; + configList=glxChooseFBConfig(display, screennr, visualAttrBuffer,&nitems); + } + if (!configList) + { + visualAttrBuffer[17] = 0; + visualAttrBuffer[19] = 0; + configList=glxChooseFBConfig(display, screennr, visualAttrBuffer,&nitems); + if (configList) + { + os::Printer::log("No FSAA available.", ELL_WARNING); + CreationParams.AntiAlias=0; + } + else + { + //reenable multisampling + visualAttrBuffer[17] = 1; + visualAttrBuffer[19] = CreationParams.AntiAlias; + } + } + } + } // Next try with flipped stencil buffer value // If the first round was with stencil flag it's now without // Other way round also makes sense because some configs From dec9948d0bf04425cdc0f381f02a779502d97811 Mon Sep 17 00:00:00 2001 From: Deve Date: Wed, 23 Aug 2017 00:13:57 +0200 Subject: [PATCH 061/125] Make sure we don't write out of array. It shouldn't happen, but code below assumes that visualAttrBuffer has its full size, so just in case... --- lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp index e253cecb6..d4c6735bc 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp @@ -673,11 +673,15 @@ bool CIrrDeviceLinux::createWindow() #elif defined(GLX_SGIS_multisample) GLX_SAMPLE_BUFFERS_SGIS, 1, GLX_SAMPLES_SGIS, CreationParams.AntiAlias, // 18,19 +#else +#error #endif #ifdef GLX_ARB_framebuffer_sRGB GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, CreationParams.HandleSRGB, #elif defined(GLX_EXT_framebuffer_sRGB) GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT, CreationParams.HandleSRGB, +#else +#error #endif GLX_STEREO, CreationParams.Stereobuffer?True:False, None From 6465cc45692916ea7ef5e4c658ca3e3f6182587d Mon Sep 17 00:00:00 2001 From: Deve Date: Wed, 23 Aug 2017 21:18:45 +0200 Subject: [PATCH 062/125] Minor fix in graphics restrictions. Some android restrictions are very basic, eg. UniformBufferObject is disabled on whole platform. So make sure that it's not used on any exotic OS. --- src/graphics/graphics_restrictions.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/graphics/graphics_restrictions.cpp b/src/graphics/graphics_restrictions.cpp index 18b78bada..cfebc4c1a 100644 --- a/src/graphics/graphics_restrictions.cpp +++ b/src/graphics/graphics_restrictions.cpp @@ -365,18 +365,16 @@ public: { #if defined(__linux__) && !defined(ANDROID) if(m_os!="linux") return false; -#endif -#ifdef WIN32 +#elif defined(WIN32) if(m_os!="windows") return false; -#endif -#ifdef __APPLE__ +#elif defined(__APPLE__) if(m_os!="osx") return false; -#endif -#ifdef BSD +#elif defined(BSD) if(m_os!="bsd") return false; -#endif -#ifdef ANDROID +#elif defined(ANDROID) if(m_os!="android") return false; +#else + return false; #endif } // m_os.size()>0 From e852714466f0817aa898886d377ad7461980253d Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Wed, 23 Aug 2017 18:52:10 -0400 Subject: [PATCH 063/125] Update challenges for new city track --- data/challenges/city.challenge | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/challenges/city.challenge b/data/challenges/city.challenge index e75b552e7..0d61874e3 100644 --- a/data/challenges/city.challenge +++ b/data/challenges/city.challenge @@ -1,6 +1,6 @@ - + From 5999e83cba13839cf2eac91a81ec5f1ea210fb9d Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Wed, 23 Aug 2017 19:02:19 -0400 Subject: [PATCH 064/125] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba326e73e..63bc7e02b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ * Reduced RAM and VRAM usage, reducing load times by Auria and Benau * New kart Wilbert by Jymis * New kart Hexley by Jymis -* New track Cornfield Crossing by samuncle +* New tracks Cornfield Crossing and Candela City by samuncle * Physics improvements and various physics bugfixes by hiker * Kart GFX improvements (exhaust and headlight) * In-game screen recording feature by Benau and Stragus From 7b571de39552dc71df66fe0b35636145ec87123d Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 25 Aug 2017 16:39:34 +0800 Subject: [PATCH 065/125] Fix the wrong "misuse" Now you can play hot.ogg --- src/graphics/material.cpp | 8 ++++---- src/io/file_manager.cpp | 34 ++++++++++++++++++++++++++-------- src/io/file_manager.hpp | 2 ++ 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/src/graphics/material.cpp b/src/graphics/material.cpp index 57878c9bc..21776ff7d 100644 --- a/src/graphics/material.cpp +++ b/src/graphics/material.cpp @@ -618,10 +618,10 @@ void Material::initCustomSFX(const XMLNode *sfx) { // The directory for the track was added to the model search path - // so just misuse the getModelFile function - const std::string full_path = file_manager->getAsset(FileManager::MODEL, - filename); - SFXBuffer* buffer = SFXManager::get()->loadSingleSfx(sfx, full_path); + // so just misuse the searchModel function + std::string path = file_manager->searchModel(filename); + path = StringUtils::getPath(path); + SFXBuffer* buffer = SFXManager::get()->loadSingleSfx(sfx, path); if (buffer != NULL) { diff --git a/src/io/file_manager.cpp b/src/io/file_manager.cpp index 8e29049cc..8aea5e793 100644 --- a/src/io/file_manager.cpp +++ b/src/io/file_manager.cpp @@ -601,14 +601,14 @@ bool FileManager::findFile(std::string& full_path, return false; } // findFile - //----------------------------------------------------------------------------- - /** Tries to find the specified file in any of the given search paths. - * \param full_path On return contains the full path of the file, or - * "" if the file is not found. - * \param file_name The name of the file to look for. - * \param search_path The list of paths to search for the file. - * \return True if the file is found, false otherwise. - */ +//----------------------------------------------------------------------------- +/** Tries to find the specified file in any of the given search paths. +* \param full_path On return contains the full path of the file, or +* "" if the file is not found. +* \param file_name The name of the file to look for. +* \param search_path The list of paths to search for the file. +* \return True if the file is found, false otherwise. +*/ bool FileManager::findFile(std::string& full_path, const std::string& file_name, const std::vector& search_path) const @@ -1231,6 +1231,24 @@ std::string FileManager::searchMusic(const std::string& file_name) const return path; } // searchMusic +//----------------------------------------------------------------------------- +/** Returns the full path of a model file by searching all model search paths. + * It throws an exception if the file is not found. + * \param file_name File name to search for. + */ +std::string FileManager::searchModel(const std::string& file_name) const +{ + std::string path; + bool success = findFile(path, file_name, m_model_search_path); + if (!success) + { + throw std::runtime_error( + "[FileManager::searchModel] Cannot find model file '" + +file_name+"'."); + } + return path; +} // searchModel + //----------------------------------------------------------------------------- /** Returns true if the given name is a directory. * \param path File name to test. diff --git a/src/io/file_manager.hpp b/src/io/file_manager.hpp index 26e56f165..6c766e3de 100644 --- a/src/io/file_manager.hpp +++ b/src/io/file_manager.hpp @@ -162,6 +162,8 @@ public: } // ------------------------------------------------------------------------ std::string searchMusic(const std::string& file_name) const; + // ------------------------------------------------------------------------ + std::string searchModel(const std::string& file_name) const; std::string searchTexture(const std::string& fname) const; std::string getUserConfigFile(const std::string& fname) const; bool fileExists(const std::string& path) const; From ea58a736d5b1f4d6f56e81e5bdc85b542d664091 Mon Sep 17 00:00:00 2001 From: Benau Date: Mon, 28 Aug 2017 13:04:41 +0800 Subject: [PATCH 066/125] Fix displace fallback in non-advanced pipeline --- src/graphics/stk_mesh.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/graphics/stk_mesh.cpp b/src/graphics/stk_mesh.cpp index 87e2e2b29..a7a3bcd7d 100644 --- a/src/graphics/stk_mesh.cpp +++ b/src/graphics/stk_mesh.cpp @@ -84,8 +84,10 @@ TransparentMaterial getTransparentMaterialFromType(video::E_MATERIAL_TYPE type, { if (CVS->isDefferedEnabled()) return TM_DISPLACEMENT; - else + else if (tp == video::EVT_2TCOORDS) return TM_TRANSLUCENT_2TC; + else + return TM_TRANSLUCENT_STD; } if (material->getShaderType() == Material::SHADERTYPE_ADDITIVE) return TM_ADDITIVE; From 704d3ea9d99dff49ccfa838ea3d6b1dc45937459 Mon Sep 17 00:00:00 2001 From: Deve Date: Mon, 28 Aug 2017 21:10:09 +0200 Subject: [PATCH 067/125] Try to make touch device a bit more comfortable --- src/input/multitouch_device.cpp | 35 +++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/input/multitouch_device.cpp b/src/input/multitouch_device.cpp index e73bfefd5..cbd6f8610 100644 --- a/src/input/multitouch_device.cpp +++ b/src/input/multitouch_device.cpp @@ -157,7 +157,6 @@ void MultitouchDevice::addButton(MultitouchButtonType type, int x, int y, m_accelerometer_active = true; } } - } #endif } // addButton @@ -193,32 +192,38 @@ void MultitouchDevice::updateDeviceState(unsigned int event_id) { assert(event_id < m_events.size()); - MultitouchEvent event = m_events[event_id]; + MultitouchButton* pressed_button = NULL; + + for (MultitouchButton* button : m_buttons) + { + if (button->pressed && button->event_id == event_id) + { + pressed_button = button; + break; + } + } for (MultitouchButton* button : m_buttons) { + if (pressed_button != NULL && button != pressed_button) + continue; + bool update_controls = false; bool prev_button_state = button->pressed; - float prev_axis_x = button->axis_x; - float prev_axis_y = button->axis_y; + MultitouchEvent event = m_events[event_id]; - if (event.x < button->x || event.x > button->x + button->width || - event.y < button->y || event.y > button->y + button->height) - { - if (button->event_id == event_id) - { - button->pressed = false; - button->event_id = 0; - updateButtonAxes(button, 0.0f, 0.0f); - } - } - else + if (pressed_button != NULL || + (event.x >= button->x && event.x <= button->x + button->width && + event.y >= button->y && event.y <= button->y + button->height)) { button->pressed = event.touched; button->event_id = event_id; if (button->type == MultitouchButtonType::BUTTON_STEERING) { + float prev_axis_x = button->axis_x; + float prev_axis_y = button->axis_y; + if (button->pressed == true) { updateButtonAxes(button, From 2fdb33990ed71a36a4323364c370dad3e8e3f62d Mon Sep 17 00:00:00 2001 From: Deve Date: Mon, 28 Aug 2017 22:24:57 +0200 Subject: [PATCH 068/125] Tweak some touch device params. Better on 5'', worse on 10''... Can be changed in options anyway. --- src/config/user_config.hpp | 4 ++-- src/states_screens/race_gui_multitouch.cpp | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/config/user_config.hpp b/src/config/user_config.hpp index 954848541..d59706ab3 100644 --- a/src/config/user_config.hpp +++ b/src/config/user_config.hpp @@ -431,13 +431,13 @@ namespace UserConfigParams "considered as centered in steering button.")); PARAM_PREFIX FloatUserConfigParam m_multitouch_deadzone_edge - PARAM_DEFAULT( FloatUserConfigParam(0.25f, "multitouch_deadzone_edge", + PARAM_DEFAULT( FloatUserConfigParam(0.1f, "multitouch_deadzone_edge", &m_multitouch_group, "A parameter in range [0, 0.5] that determines the zone that is " "considered as max value in steering button.")); PARAM_PREFIX FloatUserConfigParam m_multitouch_scale - PARAM_DEFAULT( FloatUserConfigParam(1.0f, "multitouch_scale", + PARAM_DEFAULT( FloatUserConfigParam(1.1f, "multitouch_scale", &m_multitouch_group, "A parameter in range [0.5, 1.5] that determines the scale of the " "multitouch interface.")); diff --git a/src/states_screens/race_gui_multitouch.cpp b/src/states_screens/race_gui_multitouch.cpp index fbf0fa474..0a906eb49 100644 --- a/src/states_screens/race_gui_multitouch.cpp +++ b/src/states_screens/race_gui_multitouch.cpp @@ -114,19 +114,21 @@ void RaceGUIMultitouch::initMultitouchSteering() float first_column_x = w - 2 * col_size; float second_column_x = w - 1 * col_size; - float steering_btn_x = 0.5f * margin; + float steering_btn_margin = 0.6f * margin; + float steering_btn_x = steering_btn_margin; + float steering_btn_y = h - steering_btn_margin - btn2_size; if (UserConfigParams::m_multitouch_inverted) { first_column_x = margin + 1 * col_size; second_column_x = margin; - steering_btn_x = w - btn2_size - 0.5f * margin; + steering_btn_x = w - btn2_size - steering_btn_margin; } m_minimap_bottom = (unsigned int)(h - 2 * col_size); m_device->addButton(BUTTON_STEERING, - int(steering_btn_x), int(h - 0.5f * margin - btn2_size), + int(steering_btn_x), int(steering_btn_y), int(btn2_size), int(btn2_size)); m_device->addButton(BUTTON_ESCAPE, int(margin_top), int(margin_small), From cf98e049b01604f527e23fb8ddfbe12b31237c39 Mon Sep 17 00:00:00 2001 From: hiker Date: Tue, 29 Aug 2017 08:23:06 +1000 Subject: [PATCH 069/125] Fixed sliding (i.e. not being able to drive up steep uphill sections). Fixes #2906. --- src/karts/kart.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 3d9e8c684..6379915c9 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -2509,13 +2509,18 @@ void Kart::updateSliding() // Now test for each wheel if it should be sliding // ----------------------------------------------- bool enable_sliding = false; + + // We need the 'up' vector, which can be affected by material + // with gravity. So use the body's gravity to determine up: + Vec3 up = -m_body->getGravity(); + up.normalize(); for (int i = 0; i < m_vehicle->getNumWheels(); i++) { const btWheelInfo &wheel = m_vehicle->getWheelInfo(i); if (!wheel.m_raycastInfo.m_isInContact) continue; const btVector3 &norm = m_vehicle->getWheelInfo(i).m_raycastInfo.m_contactNormalWS; - float distanceFromUp = norm.dot(getNormal()); + float distanceFromUp = norm.dot(up); float friction; if (distanceFromUp < 0.85f) { From 96c4e26ef3ebe447a25e37e02a71f330877b4364 Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Mon, 28 Aug 2017 19:36:34 -0400 Subject: [PATCH 070/125] Update strings --- data/po/supertuxkart.pot | 595 ++++++++++++++++++++++++--------------- 1 file changed, 365 insertions(+), 230 deletions(-) diff --git a/data/po/supertuxkart.pot b/data/po/supertuxkart.pot index 08bddec9a..0444ecccb 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 20:00-0400\n" +"POT-Creation-Date: 2017-08-28 19:28-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -181,7 +181,7 @@ msgstr "" #. I18N: track group #. I18N: ./data/gui/easter_egg.stkgui #. I18N: track group -#: src/states_screens/grand_prix_editor_screen.cpp:325 +#: src/states_screens/grand_prix_editor_screen.cpp:330 msgid "Standard" msgstr "" @@ -195,7 +195,7 @@ msgstr "" #. I18N: track group name #: src/states_screens/arenas_screen.cpp:83 #: src/states_screens/easter_egg_screen.cpp:145 -#: src/states_screens/grand_prix_editor_screen.cpp:327 +#: src/states_screens/grand_prix_editor_screen.cpp:332 #: src/states_screens/kart_selection.cpp:286 #: src/states_screens/tracks_and_gp_screen.cpp:148 msgid "Add-Ons" @@ -215,18 +215,18 @@ msgstr "" #: src/states_screens/easter_egg_screen.cpp:137 #: src/states_screens/edit_track_screen.cpp:147 #: src/states_screens/gp_info_screen.cpp:76 -#: src/states_screens/grand_prix_editor_screen.cpp:324 +#: src/states_screens/grand_prix_editor_screen.cpp:329 #: src/states_screens/kart_selection.cpp:278 -#: src/states_screens/options_screen_video.cpp:389 +#: src/states_screens/options_screen_video.cpp:456 #: src/states_screens/tracks_and_gp_screen.cpp:138 -#: src/states_screens/tracks_screen.cpp:142 +#: src/states_screens/tracks_screen.cpp:141 msgid "All" msgstr "" #. I18N: ./data/gui/confirm_dialog.stkgui #. I18N: In a 'are you sure?' dialog #: src/states_screens/edit_gp_screen.cpp:257 -#: src/states_screens/ghost_replay_selection.cpp:116 +#: src/states_screens/ghost_replay_selection.cpp:117 msgid "Yes" msgstr "" @@ -235,8 +235,8 @@ msgstr "" #. I18N: ./data/gui/confirm_resolution_dialog.stkgui #. I18N: In the 'confirm resolution' dialog, that's shown when switching resoluton #. I18N: ./data/gui/edit_track.stkgui -#. I18N: ./data/gui/enter_gp_name_dialog.stkgui -#. I18N: In the 'add new grand prix' dialog +#. I18N: ./data/gui/general_text_field_dialog.stkgui +#. I18N: In the general textfield dialog #. I18N: ./data/gui/online/create_server.stkgui #. I18N: In the server creation screen #. I18N: ./data/gui/online/recovery_input.stkgui @@ -341,11 +341,6 @@ msgstr "" msgid "Texture compression" msgstr "" -#. I18N: ./data/gui/custom_video_settings.stkgui -#. I18N: Video settings -msgid "Use high definition textures" -msgstr "" - #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings msgid "Animated Characters" @@ -353,7 +348,12 @@ msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui #. I18N: Video settings -msgid "Texture filtering" +msgid "Rendered image quality" +msgstr "" + +#. I18N: ./data/gui/custom_video_settings.stkgui +#. I18N: Video settings +msgid "Geometry detail" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui @@ -362,6 +362,7 @@ msgid "* Restart STK to apply new settings" msgstr "" #. I18N: ./data/gui/custom_video_settings.stkgui +#. I18N: ./data/gui/multitouch_settings.stkgui msgid "Apply" msgstr "" @@ -428,8 +429,8 @@ msgid "Reverse:" msgstr "" #. I18N: ./data/gui/edit_track.stkgui -#. I18N: ./data/gui/enter_gp_name_dialog.stkgui -#. I18N: In the 'add new grand prix' dialog +#. I18N: ./data/gui/general_text_field_dialog.stkgui +#. I18N: In the general textfield dialog #. I18N: ./data/gui/online/register.stkgui #. I18N: In the registration dialog #. I18N: ./data/gui/user_screen.stkgui @@ -441,11 +442,6 @@ msgstr "" msgid "OK" msgstr "" -#. I18N: ./data/gui/enter_gp_name_dialog.stkgui -#. I18N: In the 'add new grand prix' dialog -msgid "Please enter the name of the grand prix" -msgstr "" - #. I18N: ./data/gui/feature_unlocked.stkgui #. I18N: ./data/gui/grand_prix_lose.stkgui #. I18N: ./data/gui/grand_prix_win.stkgui @@ -550,6 +546,7 @@ msgstr "" #. I18N: ./data/gui/help2.stkgui #. I18N: ./data/gui/help3.stkgui #. I18N: ./data/gui/help4.stkgui +#. I18N: ./data/gui/help5.stkgui msgid "SuperTuxKart Help" msgstr "" @@ -561,6 +558,10 @@ msgstr "" #. I18N: Tab in help menu #. I18N: ./data/gui/help4.stkgui #. I18N: Tab in help menu +#. I18N: ./data/gui/help5.stkgui +#. I18N: Tab in help menu +#. I18N: ./data/gui/multitouch_settings.stkgui +#. I18N: In the multitouch settings screen msgid "General" msgstr "" @@ -572,6 +573,8 @@ msgstr "" #. I18N: Tab in help menu #. I18N: ./data/gui/help4.stkgui #. I18N: Tab in help menu +#. I18N: ./data/gui/help5.stkgui +#. I18N: Tab in help menu msgid "Weapons" msgstr "" @@ -583,6 +586,8 @@ msgstr "" #. I18N: Tab in help menu #. I18N: ./data/gui/help4.stkgui #. I18N: Tab in help menu +#. I18N: ./data/gui/help5.stkgui +#. I18N: Tab in help menu msgid "Game Modes" msgstr "" @@ -594,11 +599,26 @@ msgstr "" #. I18N: Tab in help menu #. I18N: ./data/gui/help4.stkgui #. I18N: Tab in help menu +#. I18N: ./data/gui/help5.stkgui +#. I18N: Tab in help menu msgid "Multi-player" msgstr "" #. I18N: ./data/gui/help1.stkgui -msgid "Click here to play the tutorial" +#. I18N: Tab in help menu +#. I18N: ./data/gui/help2.stkgui +#. I18N: Tab in help menu +#. I18N: ./data/gui/help3.stkgui +#. I18N: Tab in help menu +#. I18N: ./data/gui/help4.stkgui +#. I18N: Tab in help menu +#. I18N: ./data/gui/help5.stkgui +#. I18N: Tab in help menu +msgid "Bananas" +msgstr "" + +#. I18N: ./data/gui/help1.stkgui +msgid "Start the tutorial" msgstr "" #. I18N: ./data/gui/help1.stkgui @@ -767,6 +787,29 @@ msgid "" "not be used for this operation." msgstr "" +#. I18N: ./data/gui/help5.stkgui +msgid "" +"Hitting a banana can result in one of the following being attached to the " +"kart:" +msgstr "" + +#. I18N: ./data/gui/help5.stkgui +#. I18N: In the help menu +msgid "Anchor - slows down the kart." +msgstr "" + +#. I18N: ./data/gui/help5.stkgui +#. I18N: In the help menu +msgid "Parachute - slows down the kart less than the anchor." +msgstr "" + +#. I18N: ./data/gui/help5.stkgui +#. I18N: In the help menu +msgid "" +"Bomb - detonates after a short amount of time to throw the kart up in the " +"air. Bump into another kart to transfer the bomb to another player." +msgstr "" + #. I18N: ./data/gui/karts.stkgui #. I18N: In the kart selection (player setup) screen #. I18N: ./data/gui/karts_online.stkgui @@ -825,7 +868,7 @@ msgstr "" #. I18N: ./data/gui/main_menu.stkgui #. I18N: In the main screen -#: src/states_screens/race_gui_overworld.cpp:465 +#: src/states_screens/race_gui_overworld.cpp:503 msgid "Tutorial" msgstr "" @@ -852,6 +895,49 @@ msgstr "" msgid "Quit" msgstr "" +#. I18N: ./data/gui/multitouch_settings.stkgui +msgid "Touch Device Settings" +msgstr "" + +#. I18N: ./data/gui/multitouch_settings.stkgui +#. I18N: In the multitouch settings screen +msgid "Device enabled" +msgstr "" + +#. I18N: ./data/gui/multitouch_settings.stkgui +#. I18N: In the multitouch settings screen +msgid "Inverted buttons" +msgstr "" + +#. I18N: ./data/gui/multitouch_settings.stkgui +#. I18N: In the multitouch settings screen +msgid "Buttons scale" +msgstr "" + +#. I18N: ./data/gui/multitouch_settings.stkgui +#. I18N: In the multitouch settings screen +msgid "Accelerometer" +msgstr "" + +#. I18N: ./data/gui/multitouch_settings.stkgui +#. I18N: In the multitouch settings screen +msgid "Advanced" +msgstr "" + +#. I18N: ./data/gui/multitouch_settings.stkgui +#. I18N: In the multitouch settings screen +msgid "Deadzone center" +msgstr "" + +#. I18N: ./data/gui/multitouch_settings.stkgui +#. I18N: In the multitouch settings screen +msgid "Deadzone edge" +msgstr "" + +#. I18N: ./data/gui/multitouch_settings.stkgui +msgid "Restore defaults" +msgstr "" + #. I18N: ./data/gui/online/change_password.stkgui #. I18N: In the change password dialog msgid "Password Change" @@ -933,8 +1019,8 @@ msgstr "" #. I18N: Difficulty #. I18N: ./data/gui/select_challenge.stkgui #. I18N: Difficulty -#: src/race/race_manager.hpp:544 -#: src/states_screens/ghost_replay_selection.cpp:120 +#: src/race/race_manager.hpp:546 +#: src/states_screens/ghost_replay_selection.cpp:121 msgid "Novice" msgstr "" @@ -944,8 +1030,8 @@ msgstr "" #. I18N: Difficulty #. I18N: ./data/gui/select_challenge.stkgui #. I18N: Difficulty -#: src/race/race_manager.hpp:545 -#: src/states_screens/ghost_replay_selection.cpp:120 +#: src/race/race_manager.hpp:547 +#: src/states_screens/ghost_replay_selection.cpp:121 msgid "Intermediate" msgstr "" @@ -955,8 +1041,8 @@ msgstr "" #. I18N: Difficulty #. I18N: ./data/gui/select_challenge.stkgui #. I18N: Difficulty -#: src/race/race_manager.hpp:546 -#: src/states_screens/ghost_replay_selection.cpp:119 +#: src/race/race_manager.hpp:548 +#: src/states_screens/ghost_replay_selection.cpp:120 msgid "Expert" msgstr "" @@ -964,8 +1050,8 @@ msgstr "" #. I18N: Difficulty #. I18N: ./data/gui/race_setup.stkgui #. I18N: Difficulty -#: src/race/race_manager.hpp:547 -#: src/states_screens/ghost_replay_selection.cpp:118 +#: src/race/race_manager.hpp:549 +#: src/states_screens/ghost_replay_selection.cpp:119 msgid "SuperTux" msgstr "" @@ -1317,9 +1403,9 @@ msgstr "" #. I18N: ./data/gui/options_audio.stkgui #. 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:122 -#: src/states_screens/options_screen_video.cpp:172 +#: src/states_screens/options_screen_input.cpp:150 +#: src/states_screens/options_screen_ui.cpp:123 +#: src/states_screens/options_screen_video.cpp:238 #: src/states_screens/user_screen.cpp:660 msgid "Audio" msgstr "" @@ -1333,7 +1419,7 @@ msgstr "" #. I18N: In the audio options screen #. I18N: in the graphical options tooltip; #. indicates a graphical feature is enabled -#: src/states_screens/options_screen_video.cpp:384 +#: src/states_screens/options_screen_video.cpp:451 msgid "Enabled" msgstr "" @@ -1352,8 +1438,8 @@ 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:124 -#: src/states_screens/options_screen_video.cpp:175 +#: src/states_screens/options_screen_ui.cpp:125 +#: src/states_screens/options_screen_video.cpp:241 #: src/states_screens/user_screen.cpp:662 msgid "Controls" msgstr "" @@ -1393,9 +1479,9 @@ msgstr "" #: src/states_screens/ghost_replay_selection.cpp:78 #: 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:123 -#: src/states_screens/options_screen_video.cpp:174 +#: src/states_screens/options_screen_input.cpp:152 +#: src/states_screens/options_screen_ui.cpp:124 +#: src/states_screens/options_screen_video.cpp:240 #: src/states_screens/server_selection.cpp:103 msgid "Players" msgstr "" @@ -1418,8 +1504,8 @@ msgstr "" #. I18N: Section in the settings menu #: src/states_screens/options_screen_audio.cpp:66 #: src/states_screens/options_screen_device.cpp:87 -#: src/states_screens/options_screen_input.cpp:141 -#: src/states_screens/options_screen_video.cpp:173 +#: src/states_screens/options_screen_input.cpp:151 +#: src/states_screens/options_screen_video.cpp:239 #: src/states_screens/user_screen.cpp:661 msgid "User Interface" msgstr "" @@ -1458,8 +1544,8 @@ msgstr "" #. I18N: Section in the settings menu #: 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:121 +#: src/states_screens/options_screen_input.cpp:149 +#: src/states_screens/options_screen_ui.cpp:122 #: src/states_screens/user_screen.cpp:659 msgid "Graphics" msgstr "" @@ -1668,20 +1754,20 @@ msgstr "" msgid "Battle Island" msgstr "" -#. I18N: ../stk-assets/tracks/cave/track.xml -msgid "Cave X" +#. I18N: ../stk-assets/tracks/candela_city/track.xml +msgid "Candela City" msgstr "" -#. I18N: ../stk-assets/tracks/city/track.xml -msgid "Shiny Suburbs" +#. I18N: ../stk-assets/tracks/cave/track.xml +msgid "Cave X" msgstr "" #. I18N: ../stk-assets/tracks/cocoa_temple/track.xml msgid "Cocoa Temple" msgstr "" -#. I18N: ../stk-assets/tracks/farm/track.xml -msgid "Bovine Barnyard" +#. I18N: ../stk-assets/tracks/cornfield_crossing/track.xml +msgid "Cornfield Crossing" msgstr "" #. I18N: ../stk-assets/tracks/fortmagma/track.xml @@ -1872,18 +1958,18 @@ msgstr "" msgid "Completed achievement \"%s\"." msgstr "" -#: src/addons/addons_manager.cpp:104 src/addons/news_manager.cpp:325 -msgid "Can't access stkaddons server..." +#: src/addons/addons_manager.cpp:104 src/addons/news_manager.cpp:322 +msgid "Failed to connect to the SuperTuxKart add-ons server." msgstr "" -#: src/addons/news_manager.cpp:182 +#: src/addons/news_manager.cpp:179 #, c-format msgid "Error downloading news: '%s'." msgstr "" #. I18N: number of laps to race in a challenge #: src/challenges/challenge_data.cpp:266 -#: src/states_screens/race_result_gui.cpp:1422 +#: src/states_screens/race_result_gui.cpp:1424 #, c-format msgid "Laps: %i" msgstr "" @@ -1897,22 +1983,22 @@ msgstr "" msgid "New track '%s' now available" msgstr "" -#: src/challenges/challenge_data.cpp:523 +#: src/challenges/challenge_data.cpp:522 #, c-format msgid "New game mode '%s' now available" msgstr "" -#: src/challenges/challenge_data.cpp:533 +#: src/challenges/challenge_data.cpp:532 #, c-format msgid "New Grand Prix '%s' now available" msgstr "" -#: src/challenges/challenge_data.cpp:537 +#: src/challenges/challenge_data.cpp:536 #, c-format msgid "New difficulty '%s' now available" msgstr "" -#: src/challenges/challenge_data.cpp:547 +#: src/challenges/challenge_data.cpp:546 #, c-format msgid "New kart '%s' now available" msgstr "" @@ -1944,40 +2030,48 @@ msgid "" "created." msgstr "" -#: src/graphics/irr_driver.cpp:1950 -#, c-format -msgid "FPS: %d/%d/%d - PolyCount: %d Solid, %d Shadows - LightDist : %d" +#: src/graphics/irr_driver.cpp:618 +msgid "Video recording started." msgstr "" -#: src/graphics/irr_driver.cpp:1961 +#: src/graphics/irr_driver.cpp:624 +#, c-format +msgid "Video saved in \"%s\"." +msgstr "" + +#: src/graphics/irr_driver.cpp:628 +msgid "Encoding progress:" +msgstr "" + +#: src/graphics/irr_driver.cpp:1801 #, c-format msgid "FPS: %d/%d/%d - %d KTris" msgstr "" -#: src/guiengine/engine.cpp:1289 +#: src/guiengine/engine.cpp:1296 msgid "Loading" msgstr "" -#: src/guiengine/widgets/kart_stats_widget.cpp:89 +#: src/guiengine/widgets/kart_stats_widget.cpp:108 msgid "WEIGHT" msgstr "" -#: src/guiengine/widgets/kart_stats_widget.cpp:94 +#: src/guiengine/widgets/kart_stats_widget.cpp:113 msgid "SPEED" msgstr "" -#: src/guiengine/widgets/kart_stats_widget.cpp:98 +#: src/guiengine/widgets/kart_stats_widget.cpp:117 msgid "POWER" msgstr "" #. I18N: 'handicapped' indicates that per-player handicaps are #. activated for this kart (i.e. it will drive slower) -#: src/guiengine/widgets/player_kart_widget.cpp:361 +#: src/guiengine/widgets/player_kart_widget.cpp:381 #, c-format msgid "%s (handicapped)" msgstr "" -#: src/guiengine/widgets/player_kart_widget.cpp:420 +#: src/guiengine/widgets/player_kart_widget.cpp:440 #, c-format msgid "%s is ready" msgstr "" @@ -2469,11 +2563,11 @@ msgid "Mouse axis %d %s" msgstr "" #. I18N: shown when config file is too old -#: src/input/device_manager.cpp:501 +#: src/input/device_manager.cpp:543 msgid "Please re-configure your key bindings." msgstr "" -#: src/input/device_manager.cpp:502 +#: src/input/device_manager.cpp:544 msgid "Your input config file is not compatible with this version of STK." msgstr "" @@ -2495,51 +2589,51 @@ msgstr "" #. I18N: name of buttons on gamepads #. I18N: name of stick on gamepads -#: src/input/gamepad_config.cpp:181 src/input/gamepad_config.cpp:247 +#: src/input/gamepad_config.cpp:181 src/input/gamepad_config.cpp:244 msgid "Right thumb right" msgstr "" #. I18N: name of buttons on gamepads #. I18N: name of stick on gamepads -#: src/input/gamepad_config.cpp:183 src/input/gamepad_config.cpp:249 +#: src/input/gamepad_config.cpp:183 src/input/gamepad_config.cpp:246 msgid "Right thumb left" msgstr "" #. I18N: name of buttons on gamepads -#. I18N: name of stick on gamepads -#: src/input/gamepad_config.cpp:185 src/input/gamepad_config.cpp:243 +#. I18N: name of trigger on gamepads +#: src/input/gamepad_config.cpp:185 src/input/gamepad_config.cpp:240 msgid "Right thumb down" msgstr "" #. I18N: name of buttons on gamepads #. I18N: name of stick on gamepads -#: src/input/gamepad_config.cpp:187 src/input/gamepad_config.cpp:245 +#: src/input/gamepad_config.cpp:187 src/input/gamepad_config.cpp:242 msgid "Right thumb up" msgstr "" #. I18N: name of buttons on gamepads -#. I18N: name of stick on gamepads -#: src/input/gamepad_config.cpp:189 src/input/gamepad_config.cpp:241 +#: src/input/gamepad_config.cpp:189 src/input/gamepad_config.cpp:248 msgid "Right trigger" msgstr "" #. I18N: name of buttons on gamepads -#: src/input/gamepad_config.cpp:191 src/input/gamepad_config.cpp:255 +#: src/input/gamepad_config.cpp:191 src/input/gamepad_config.cpp:253 msgid "DPad right" msgstr "" #. I18N: name of buttons on gamepads -#: src/input/gamepad_config.cpp:193 src/input/gamepad_config.cpp:257 +#: src/input/gamepad_config.cpp:193 src/input/gamepad_config.cpp:255 msgid "DPad left" msgstr "" #. I18N: name of buttons on gamepads -#: src/input/gamepad_config.cpp:195 src/input/gamepad_config.cpp:253 +#: src/input/gamepad_config.cpp:195 src/input/gamepad_config.cpp:251 msgid "DPad down" msgstr "" #. I18N: name of buttons on gamepads -#: src/input/gamepad_config.cpp:197 src/input/gamepad_config.cpp:251 +#. I18N: name of trigger on gamepads +#: src/input/gamepad_config.cpp:197 src/input/gamepad_config.cpp:249 msgid "DPad up" msgstr "" @@ -2588,55 +2682,63 @@ msgstr "" msgid "Left thumb up" msgstr "" -#: src/input/input_manager.cpp:768 +#: src/input/input_manager.cpp:807 #, c-format msgid "Ignoring '%s'. You needed to join earlier to play!" msgstr "" -#: src/input/input_manager.cpp:798 +#: src/input/input_manager.cpp:837 msgid "Only the Game Master may act at this point!" msgstr "" -#: src/input/wiimote_manager.cpp:389 +#: src/input/wiimote_manager.cpp:388 msgid "" "Connect your wiimote to the Bluetooth manager, then click on Ok. Detailed " "instructions at supertuxkart.net/Wiimote" msgstr "" -#: src/input/wiimote_manager.cpp:392 +#: src/input/wiimote_manager.cpp:391 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" msgstr "" -#: src/input/wiimote_manager.cpp:415 +#: src/input/wiimote_manager.cpp:414 #, c-format msgid "Found %d wiimote" msgid_plural "Found %d wiimotes" msgstr[0] "" msgstr[1] "" -#: src/input/wiimote_manager.cpp:420 +#: src/input/wiimote_manager.cpp:419 msgid "Could not detect any wiimote :/" msgstr "" -#: src/karts/controller/local_player_controller.cpp:206 +#: src/karts/controller/local_player_controller.cpp:244 msgid "Penalty time!!" msgstr "" -#: src/karts/controller/local_player_controller.cpp:208 +#: src/karts/controller/local_player_controller.cpp:246 msgid "Don't accelerate before go" msgstr "" -#: src/karts/kart.cpp:852 src/karts/kart.cpp:857 +#: src/karts/controller/spare_tire_ai.cpp:147 +msgid "You can have at most 3 lives!" +msgstr "" + +#: src/karts/controller/spare_tire_ai.cpp:153 +msgid "+1 life." +msgstr "" + +#: src/karts/kart.cpp:903 src/karts/kart.cpp:908 msgid "You won the race!" msgstr "" -#: src/karts/kart.cpp:857 +#: src/karts/kart.cpp:908 msgid "You finished the race!" msgstr "" -#: src/main.cpp:1413 +#: src/main.cpp:1500 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 " @@ -2646,12 +2748,16 @@ msgid "" "edit \"Connect to the Internet\" and \"Send anonymous HW statistics\")." msgstr "" -#: src/main.cpp:1563 +#: src/main.cpp:1654 +msgid "Your screen resolution is too small to run STK." +msgstr "" + +#: src/main.cpp:1668 msgid "" "Your driver version is too old. Please install the latest video drivers." msgstr "" -#: src/main.cpp:1580 +#: src/main.cpp:1685 #, c-format msgid "" "Your OpenGL version appears to be too old. Please verify if an update for " @@ -2663,81 +2769,89 @@ msgstr "" msgid "Eggs: %d / %d" msgstr "" -#: src/modes/follow_the_leader.cpp:61 src/modes/follow_the_leader.cpp:284 +#: src/modes/follow_the_leader.cpp:62 src/modes/follow_the_leader.cpp:285 msgid "Leader" msgstr "" -#: src/modes/linear_world.cpp:284 +#: src/modes/linear_world.cpp:287 msgid "Final lap!" msgstr "" -#: src/modes/linear_world.cpp:311 +#: src/modes/linear_world.cpp:314 #, c-format msgid "Lap %i" msgstr "" -#: src/modes/linear_world.cpp:366 +#: src/modes/linear_world.cpp:369 #, c-format msgctxt "fastest_lap" msgid "%s by %s" msgstr "" -#: src/modes/linear_world.cpp:371 +#: src/modes/linear_world.cpp:374 msgid "New fastest lap" msgstr "" -#: src/modes/linear_world.cpp:890 +#: src/modes/linear_world.cpp:891 msgid "WRONG WAY!" msgstr "" -#: src/modes/world.cpp:1207 +#: src/modes/three_strikes_battle.cpp:664 +#, c-format +msgid "%i spare tire kart has been spawned!" +msgid_plural "%i spare tire karts have been spawned!" +msgstr[0] "" +msgstr[1] "" + +#: src/modes/world.cpp:1202 msgid "You have been eliminated!" msgstr "" -#: src/modes/world.cpp:1210 +#: src/modes/world.cpp:1205 #, c-format msgid "'%s' has been eliminated." msgstr "" -#: src/network/protocols/server_lobby_room_protocol.cpp:237 -msgid "Failed to register server" +#: src/network/protocols/server_lobby.cpp:318 +#, c-format +msgid "Failed to register server: %s" msgstr "" -#: src/network/servers_manager.cpp:197 +#: src/network/servers_manager.cpp:198 msgid "No LAN server detected" msgstr "" -#: src/online/online_player_profile.cpp:420 +#: src/online/online_player_profile.cpp:419 #, c-format msgid "%s is now online." msgstr "" -#: src/online/online_player_profile.cpp:424 +#: src/online/online_player_profile.cpp:423 #, c-format msgid "%s and %s are now online." msgstr "" -#: src/online/online_player_profile.cpp:429 +#: src/online/online_player_profile.cpp:428 #, c-format msgid "%s, %s and %s are now online." msgstr "" #. I18N: Only used for count > 3 -#: src/online/online_player_profile.cpp:435 +#: src/online/online_player_profile.cpp:434 #, c-format msgid "%d friend is now online." msgid_plural "%d friends are now online." msgstr[0] "" msgstr[1] "" -#: src/online/online_player_profile.cpp:472 +#: src/online/online_player_profile.cpp:471 #, c-format msgid "You have %d new friend request!" msgid_plural "You have %d new friend requests!" msgstr[0] "" msgstr[1] "" -#: src/online/online_player_profile.cpp:478 +#: src/online/online_player_profile.cpp:477 msgid "You have a new friend request!" msgstr "" @@ -2777,11 +2891,11 @@ msgstr "" msgid "Soccer" msgstr "" -#: src/replay/replay_recorder.cpp:182 +#: src/replay/replay_recorder.cpp:183 msgid "Incomplete replay file will not be saved." msgstr "" -#: src/replay/replay_recorder.cpp:218 +#: src/replay/replay_recorder.cpp:219 #, c-format msgid "Replay saved in \"%s\"." msgstr "" @@ -2844,7 +2958,7 @@ msgid "Please wait while addons are updated" msgstr "" #: src/states_screens/addons_screen.cpp:551 -#: src/states_screens/main_menu_screen.cpp:554 +#: src/states_screens/main_menu_screen.cpp:571 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 " @@ -2866,8 +2980,8 @@ msgstr "" #: src/states_screens/arenas_screen.cpp:327 #: src/states_screens/easter_egg_screen.cpp:225 #: src/states_screens/easter_egg_screen.cpp:256 -#: src/states_screens/kart_selection.cpp:864 -#: src/states_screens/kart_selection.cpp:1450 +#: src/states_screens/kart_selection.cpp:859 +#: src/states_screens/kart_selection.cpp:1464 #: src/states_screens/race_setup_screen.cpp:99 msgid "Locked : solve active challenges to gain access to more!" msgstr "" @@ -2904,7 +3018,7 @@ msgstr "" msgid "The maxinum number of players has to be between 2 and 12." msgstr "" -#: src/states_screens/credits.cpp:217 +#: src/states_screens/credits.cpp:180 msgid "translator-credits" msgstr "" @@ -3010,74 +3124,74 @@ msgid_plural "Confirm resolution within %i seconds" msgstr[0] "" msgstr[1] "" +#. I18N: Geometry level disabled : lowest level, no details #. I18N: in the graphical options tooltip; #. indicates a graphical feature is disabled -#: src/states_screens/dialogs/custom_video_settings.cpp:62 -#: src/states_screens/dialogs/custom_video_settings.cpp:86 -#: src/states_screens/options_screen_video.cpp:387 +#: src/states_screens/dialogs/custom_video_settings.cpp:66 +#: src/states_screens/dialogs/custom_video_settings.cpp:78 +#: src/states_screens/dialogs/custom_video_settings.cpp:95 +#: src/states_screens/dialogs/multitouch_settings_dialog.cpp:59 +#: src/states_screens/options_screen_video.cpp:454 msgid "Disabled" msgstr "" #. I18N: animations setting (only karts with human players are animated) -#: src/states_screens/dialogs/custom_video_settings.cpp:64 +#: src/states_screens/dialogs/custom_video_settings.cpp:68 msgid "Human players only" msgstr "" -#. I18N: animations setting (all karts are animated) -#: src/states_screens/dialogs/custom_video_settings.cpp:66 +#: src/states_screens/dialogs/custom_video_settings.cpp:71 msgid "Enabled for all" msgstr "" -#: src/states_screens/dialogs/custom_video_settings.cpp:76 -msgid "Bilinear" -msgstr "" - -#: src/states_screens/dialogs/custom_video_settings.cpp:77 -msgid "Trilinear" -msgstr "" - -#: src/states_screens/dialogs/custom_video_settings.cpp:78 -msgid "Anisotropic x2" -msgstr "" - -#: src/states_screens/dialogs/custom_video_settings.cpp:79 -msgid "Anisotropic x4" -msgstr "" - +#. I18N: Geometry level low : few details are displayed +#. I18N: in the graphical options tooltip; +#. indicates the rendered image quality is low #: src/states_screens/dialogs/custom_video_settings.cpp:80 -msgid "Anisotropic x8" +#: src/states_screens/dialogs/custom_video_settings.cpp:89 +#: src/states_screens/dialogs/custom_video_settings.cpp:96 +#: src/states_screens/options_screen_video.cpp:467 +msgid "Low" msgstr "" -#: src/states_screens/dialogs/custom_video_settings.cpp:81 -msgid "Anisotropic x16" -msgstr "" - -#: src/states_screens/dialogs/custom_video_settings.cpp:87 -msgid "low" +#. I18N: Geometry level high : everything is displayed +#. I18N: in the graphical options tooltip; +#. indicates the rendered image quality is high +#: src/states_screens/dialogs/custom_video_settings.cpp:82 +#: src/states_screens/dialogs/custom_video_settings.cpp:90 +#: src/states_screens/dialogs/custom_video_settings.cpp:97 +#: src/states_screens/options_screen_video.cpp:470 +msgid "High" msgstr "" +#. I18N: in the graphical options tooltip; +#. indicates the rendered image quality is very low #: src/states_screens/dialogs/custom_video_settings.cpp:88 -msgid "high" +#: src/states_screens/options_screen_video.cpp:464 +msgid "Very Low" msgstr "" -#: src/states_screens/dialogs/enter_gp_name_dialog.cpp:125 -msgid "Name is empty." -msgstr "" - -#: src/states_screens/dialogs/enter_gp_name_dialog.cpp:132 -msgid "Another grand prix with this name already exists." -msgstr "" - -#: src/states_screens/dialogs/enter_gp_name_dialog.cpp:137 -msgid "Name is too long." +#. I18N: in the graphical options tooltip; +#. indicates the rendered image quality is very high +#: src/states_screens/dialogs/custom_video_settings.cpp:91 +#: src/states_screens/options_screen_video.cpp:473 +msgid "Very High" msgstr "" #: src/states_screens/dialogs/message_dialog.cpp:129 #: src/states_screens/edit_gp_screen.cpp:257 -#: src/states_screens/ghost_replay_selection.cpp:116 +#: src/states_screens/ghost_replay_selection.cpp:117 msgid "No" msgstr "" +#: src/states_screens/dialogs/multitouch_settings_dialog.cpp:60 +msgid "Tablet" +msgstr "" + +#: src/states_screens/dialogs/multitouch_settings_dialog.cpp:61 +msgid "Phone" +msgstr "" + #: src/states_screens/dialogs/recovery_dialog.cpp:121 msgid "Username and/or email address invalid." msgstr "" @@ -3176,7 +3290,7 @@ msgstr "" #: src/states_screens/easter_egg_screen.cpp:270 #: src/states_screens/tracks_and_gp_screen.cpp:292 -#: src/states_screens/tracks_screen.cpp:242 +#: src/states_screens/tracks_screen.cpp:241 msgid "Random Track" msgstr "" @@ -3195,8 +3309,8 @@ msgid "Reversed" msgstr "" #: src/states_screens/edit_gp_screen.cpp:124 -#: src/states_screens/ghost_replay_selection.cpp:174 -#: src/states_screens/grand_prix_editor_screen.cpp:109 +#: src/states_screens/ghost_replay_selection.cpp:177 +#: src/states_screens/grand_prix_editor_screen.cpp:112 #, c-format msgid "Are you sure you want to remove '%s'?" msgstr "" @@ -3215,7 +3329,7 @@ msgstr "" msgid "An error occurred while trying to save your grand prix." msgstr "" -#: src/states_screens/edit_track_screen.cpp:237 +#: src/states_screens/edit_track_screen.cpp:235 msgid "Select a track" msgstr "" @@ -3252,13 +3366,17 @@ msgstr "" msgid "Finish Time" msgstr "" +#: src/states_screens/ghost_replay_selection.cpp:83 +msgid "User" +msgstr "" + #: src/states_screens/gp_info_screen.cpp:74 msgid "Default" msgstr "" #. I18N: if no kart animations are enabled #: src/states_screens/gp_info_screen.cpp:75 -#: src/states_screens/options_screen_video.cpp:393 +#: src/states_screens/options_screen_video.cpp:460 msgid "None" msgstr "" @@ -3271,62 +3389,80 @@ msgstr "" msgid "Reload" msgstr "" -#: src/states_screens/grand_prix_editor_screen.cpp:164 +#: src/states_screens/grand_prix_cutscene.cpp:75 +#: src/states_screens/grand_prix_editor_screen.cpp:101 +#: src/states_screens/grand_prix_editor_screen.cpp:118 +msgid "Please enter the name of the grand prix" +msgstr "" + +#: src/states_screens/grand_prix_editor_screen.cpp:169 msgid "Please select a Grand Prix" msgstr "" -#: src/states_screens/grand_prix_editor_screen.cpp:326 +#: src/states_screens/grand_prix_editor_screen.cpp:331 msgid "User defined" msgstr "" +#: src/states_screens/grand_prix_editor_screen.cpp:344 +msgid "Name is empty." +msgstr "" + +#: src/states_screens/grand_prix_editor_screen.cpp:352 +msgid "Another grand prix with this name already exists." +msgstr "" + +#: src/states_screens/grand_prix_editor_screen.cpp:358 +msgid "Name is too long." +msgstr "" + #. I18N: when failing a GP #: src/states_screens/grand_prix_lose.cpp:154 msgid "Better luck next time!" msgstr "" -#: src/states_screens/grand_prix_win.cpp:127 +#: src/states_screens/grand_prix_win.cpp:166 #: src/states_screens/race_result_gui.cpp:194 msgid "You completed a challenge!" msgstr "" -#: src/states_screens/grand_prix_win.cpp:284 +#: src/states_screens/grand_prix_win.cpp:322 msgid "You completed the Grand Prix!" msgstr "" -#: src/states_screens/kart_selection.cpp:845 -#: src/states_screens/kart_selection.cpp:1468 +#: src/states_screens/kart_selection.cpp:840 +#: src/states_screens/kart_selection.cpp:1482 msgid "Random Kart" msgstr "" -#: src/states_screens/kart_selection.cpp:859 +#: src/states_screens/kart_selection.cpp:854 msgid "Locked" msgstr "" -#: src/states_screens/kart_selection.cpp:929 +#: src/states_screens/kart_selection.cpp:944 msgid "" "Everyone:\n" "Press the 'Select' button to join the game" msgstr "" -#: src/states_screens/main_menu_screen.cpp:493 +#: src/states_screens/main_menu_screen.cpp:510 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:517 +#: src/states_screens/main_menu_screen.cpp:534 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:549 +#: src/states_screens/main_menu_screen.cpp:566 msgid "The add-ons module is currently disabled in the Options screen" msgstr "" -#: src/states_screens/main_menu_screen.cpp:561 +#: src/states_screens/main_menu_screen.cpp:578 msgid "Please wait while the add-ons are loading" msgstr "" @@ -3389,13 +3525,13 @@ msgstr "" #. I18N: button to disable a gamepad configuration #: src/states_screens/options_screen_device.cpp:96 -#: src/states_screens/options_screen_device.cpp:575 +#: src/states_screens/options_screen_device.cpp:579 msgid "Disable Device" msgstr "" #. I18N: button to enable a gamepad configuration #: src/states_screens/options_screen_device.cpp:98 -#: src/states_screens/options_screen_device.cpp:576 +#: src/states_screens/options_screen_device.cpp:580 msgid "Enable Device" msgstr "" @@ -3497,7 +3633,7 @@ msgstr "" msgid "* A red item means a conflict in the current configuration" msgstr "" -#: src/states_screens/options_screen_device.cpp:467 +#: src/states_screens/options_screen_device.cpp:471 msgid "" "Warning: The 'Shift' is not a recommended key. When 'Shift' is pressed down, " "all keys that contain a character that is different in upper-case will stop " @@ -3505,33 +3641,33 @@ msgid "" msgstr "" #. I18N: shown before deleting an input configuration -#: src/states_screens/options_screen_device.cpp:563 +#: src/states_screens/options_screen_device.cpp:567 msgid "Are you sure you want to permanently delete this configuration?" msgstr "" -#: src/states_screens/options_screen_input.cpp:97 +#: src/states_screens/options_screen_input.cpp:100 #, c-format msgid "Keyboard %i" msgstr "" -#: src/states_screens/options_screen_ui.cpp:158 +#: src/states_screens/options_screen_ui.cpp:159 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:190 +#: src/states_screens/options_screen_ui.cpp:191 msgid "System Language" msgstr "" #. I18N: custom video settings -#: src/states_screens/options_screen_video.cpp:366 +#: src/states_screens/options_screen_video.cpp:433 msgid "Custom" msgstr "" #. I18N: if some kart animations are enabled -#: src/states_screens/options_screen_video.cpp:391 +#: src/states_screens/options_screen_video.cpp:458 msgid "Me Only" msgstr "" @@ -3539,94 +3675,93 @@ msgstr "" #. tooltip = tooltip + L"\n" + _("Pixel shaders: %s", #. UserConfigParams::m_pixel_shaders ? enabled : disabled); #. I18N: in graphical options -#: src/states_screens/options_screen_video.cpp:399 +#: src/states_screens/options_screen_video.cpp:479 #, c-format msgid "Animated Scenery: %s" msgstr "" #. I18N: in graphical options -#: src/states_screens/options_screen_video.cpp:402 +#: src/states_screens/options_screen_video.cpp:482 #, c-format msgid "Weather Effects: %s" msgstr "" #. I18N: in graphical options -#: src/states_screens/options_screen_video.cpp:405 +#: src/states_screens/options_screen_video.cpp:485 #, c-format msgid "Animated Characters: %s" msgstr "" #. I18N: in graphical options -#: src/states_screens/options_screen_video.cpp:410 +#: src/states_screens/options_screen_video.cpp:490 #, c-format msgid "Dynamic lights: %s" msgstr "" #. I18N: in graphical options -#: src/states_screens/options_screen_video.cpp:413 +#: src/states_screens/options_screen_video.cpp:493 #, c-format msgid "Motion blur: %s" msgstr "" #. I18N: in graphical options -#: src/states_screens/options_screen_video.cpp:416 +#: src/states_screens/options_screen_video.cpp:496 #, c-format msgid "Anti-aliasing: %s" msgstr "" #. I18N: in graphical options -#: src/states_screens/options_screen_video.cpp:419 +#: src/states_screens/options_screen_video.cpp:499 #, c-format msgid "Ambient occlusion: %s" msgstr "" -#: src/states_screens/options_screen_video.cpp:423 +#: src/states_screens/options_screen_video.cpp:503 #, c-format msgid "Shadows: %s" msgstr "" -#: src/states_screens/options_screen_video.cpp:425 +#: src/states_screens/options_screen_video.cpp:505 #, c-format msgid "Shadows: %i" msgstr "" #. I18N: in graphical options -#: src/states_screens/options_screen_video.cpp:428 +#: src/states_screens/options_screen_video.cpp:508 #, c-format msgid "Bloom: %s" msgstr "" #. I18N: in graphical options -#: src/states_screens/options_screen_video.cpp:432 +#: src/states_screens/options_screen_video.cpp:512 #, c-format msgid "Glow (outlines): %s" msgstr "" #. I18N: in graphical options -#: src/states_screens/options_screen_video.cpp:436 +#: src/states_screens/options_screen_video.cpp:516 #, c-format msgid "Light shaft (God rays): %s" msgstr "" #. I18N: in graphical options -#: src/states_screens/options_screen_video.cpp:440 +#: src/states_screens/options_screen_video.cpp:520 #, c-format msgid "Depth of field: %s" msgstr "" #. I18N: in graphical options -#: src/states_screens/options_screen_video.cpp:444 +#: src/states_screens/options_screen_video.cpp:524 #, c-format msgid "Global illumination: %s" msgstr "" -#. I18N: in graphical options -#: src/states_screens/options_screen_video.cpp:448 +#: src/states_screens/options_screen_video.cpp:529 #, c-format -msgid "Use high definition textures: %s" +msgid "Rendered image quality: %s" msgstr "" -#: src/states_screens/race_gui.cpp:306 src/states_screens/race_gui.cpp:308 +#: src/states_screens/race_gui.cpp:350 src/states_screens/race_gui.cpp:352 msgid "Challenge Failed" msgstr "" @@ -3651,42 +3786,42 @@ 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:496 +#: src/states_screens/race_gui_base.cpp:520 msgid "by" msgstr "" -#: src/states_screens/race_gui_base.cpp:604 +#: src/states_screens/race_gui_base.cpp:629 msgid "Collect nitro!" msgstr "" -#: src/states_screens/race_gui_base.cpp:606 +#: src/states_screens/race_gui_base.cpp:631 msgid "Follow the leader!" msgstr "" #. I18N: When some GlobalPlayerIcons are hidden, write "Top 10" to show it -#: src/states_screens/race_gui_base.cpp:772 +#: src/states_screens/race_gui_base.cpp:809 #, c-format msgid "Top %i" msgstr "" #. I18N: Shown at the end of a race -#: src/states_screens/race_gui_overworld.cpp:108 +#: src/states_screens/race_gui_overworld.cpp:136 msgid "Lap" msgstr "" -#: src/states_screens/race_gui_overworld.cpp:109 +#: src/states_screens/race_gui_overworld.cpp:137 msgid "Rank" msgstr "" -#: src/states_screens/race_gui_overworld.cpp:472 -msgid "Press fire to play the tutorial" +#: src/states_screens/race_gui_overworld.cpp:510 +msgid "Press fire to start the tutorial" msgstr "" -#: src/states_screens/race_gui_overworld.cpp:511 +#: src/states_screens/race_gui_overworld.cpp:549 msgid "Type: Grand Prix" msgstr "" -#: src/states_screens/race_gui_overworld.cpp:548 +#: src/states_screens/race_gui_overworld.cpp:586 msgid "Press fire to start the challenge" msgstr "" @@ -3718,52 +3853,52 @@ msgstr "" msgid "Back to the menu" msgstr "" -#: src/states_screens/race_result_gui.cpp:373 +#: src/states_screens/race_result_gui.cpp:372 msgid "Do you really want to abort the Grand Prix?" msgstr "" -#: src/states_screens/race_result_gui.cpp:498 -#: src/states_screens/race_result_gui.cpp:861 +#: src/states_screens/race_result_gui.cpp:499 +#: src/states_screens/race_result_gui.cpp:865 msgid "Eliminated" msgstr "" -#: src/states_screens/race_result_gui.cpp:1028 +#: src/states_screens/race_result_gui.cpp:1012 msgid "Red Team Wins" msgstr "" -#: src/states_screens/race_result_gui.cpp:1032 +#: src/states_screens/race_result_gui.cpp:1016 msgid "Blue Team Wins" msgstr "" -#: src/states_screens/race_result_gui.cpp:1037 +#: src/states_screens/race_result_gui.cpp:1021 msgid "It's a draw" msgstr "" #. I18N: indicates a player that scored in their own goal in result screen -#: src/states_screens/race_result_gui.cpp:1106 +#: src/states_screens/race_result_gui.cpp:1098 #: src/states_screens/race_result_gui.cpp:1154 msgid "(Own Goal)" msgstr "" -#: src/states_screens/race_result_gui.cpp:1219 +#: src/states_screens/race_result_gui.cpp:1220 #, c-format msgid "Track %i/%i" msgstr "" -#: src/states_screens/race_result_gui.cpp:1303 +#: src/states_screens/race_result_gui.cpp:1304 msgid "Grand Prix progress:" msgstr "" -#: src/states_screens/race_result_gui.cpp:1344 +#: src/states_screens/race_result_gui.cpp:1346 msgid "Highscores" msgstr "" -#: src/states_screens/race_result_gui.cpp:1430 +#: src/states_screens/race_result_gui.cpp:1432 #, c-format msgid "Difficulty: %s" msgstr "" -#: src/states_screens/race_result_gui.cpp:1438 +#: src/states_screens/race_result_gui.cpp:1440 #, c-format msgid "Best lap time: %s" msgstr "" @@ -3835,7 +3970,7 @@ msgid "Internet access is disabled, please enable it in the options" msgstr "" #: src/states_screens/server_selection.cpp:79 -#: src/states_screens/server_selection.cpp:248 +#: src/states_screens/server_selection.cpp:247 msgid "Fetching servers" msgstr "" @@ -3849,11 +3984,11 @@ msgid "Locked!" msgstr "" #: src/states_screens/tracks_and_gp_screen.cpp:278 -#: src/states_screens/tracks_screen.cpp:228 +#: src/states_screens/tracks_screen.cpp:227 msgid "Locked: solve active challenges to gain access to more!" msgstr "" -#: src/states_screens/tracks_screen.cpp:195 +#: src/states_screens/tracks_screen.cpp:194 msgid "Only official tracks are supported." msgstr "" @@ -3871,12 +4006,12 @@ msgid "Max players supported: %d" msgstr "" #. I18N: In the track info screen -#: src/states_screens/track_info_screen.cpp:214 +#: src/states_screens/track_info_screen.cpp:213 msgid "Drive in reverse" msgstr "" #. I18N: In the track info screen -#: src/states_screens/track_info_screen.cpp:219 +#: src/states_screens/track_info_screen.cpp:218 msgid "Random item location" msgstr "" From ba55fc7d2ed249713c0810c79e3b8f8f5f5cdca5 Mon Sep 17 00:00:00 2001 From: Benau Date: Tue, 29 Aug 2017 10:31:56 +0800 Subject: [PATCH 071/125] Fix previously always true statement bufferSize (Real*BOSize) was never used and set --- src/graphics/vao_manager.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/graphics/vao_manager.cpp b/src/graphics/vao_manager.cpp index 969f55e9d..e553162d0 100644 --- a/src/graphics/vao_manager.cpp +++ b/src/graphics/vao_manager.cpp @@ -83,12 +83,12 @@ VAOManager::~VAOManager() } static void -resizeBufferIfNecessary(size_t &lastIndex, size_t newLastIndex, size_t bufferSize, size_t stride, GLenum type, GLuint &id, void *&Pointer) +resizeBufferIfNecessary(size_t &lastIndex, size_t newLastIndex, size_t& bufferSize, size_t stride, GLenum type, GLuint &id, void *&Pointer) { - if (newLastIndex * stride >= bufferSize) + if (newLastIndex >= bufferSize) { while (newLastIndex >= bufferSize) - bufferSize = 2 * bufferSize + 1; + bufferSize = bufferSize == 0 ? 1 : bufferSize * 2; GLuint newVBO; glGenBuffers(1, &newVBO); glBindBuffer(type, newVBO); From 65363a2fa001d4359d113eaeb46e5ab02a1e53a1 Mon Sep 17 00:00:00 2001 From: Deve Date: Tue, 29 Aug 2017 21:02:12 +0200 Subject: [PATCH 072/125] Move multitouch race gui creation. It's to avoid duplicated buttons because result gui uses gui_base too. --- src/states_screens/race_gui.cpp | 8 ++++++++ src/states_screens/race_gui_base.cpp | 8 -------- src/states_screens/race_gui_overworld.cpp | 8 ++++++++ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/states_screens/race_gui.cpp b/src/states_screens/race_gui.cpp index a052c7a92..3c69d9833 100644 --- a/src/states_screens/race_gui.cpp +++ b/src/states_screens/race_gui.cpp @@ -87,6 +87,13 @@ RaceGUI::RaceGUI() const float map_size = 100.0f; const float top_margin = 3.5f * m_font_height; + if (UserConfigParams::m_multitouch_enabled && + UserConfigParams::m_multitouch_mode != 0 && + race_manager->getNumLocalPlayers() == 1) + { + m_multitouch_gui = new RaceGUIMultitouch(this); + } + // Check if we have enough space for minimap when touch steering is enabled if (m_multitouch_gui != NULL) { @@ -139,6 +146,7 @@ RaceGUI::RaceGUI() //----------------------------------------------------------------------------- RaceGUI::~RaceGUI() { + delete m_multitouch_gui; } // ~Racegui diff --git a/src/states_screens/race_gui_base.cpp b/src/states_screens/race_gui_base.cpp index f89eda495..75b519ad7 100644 --- a/src/states_screens/race_gui_base.cpp +++ b/src/states_screens/race_gui_base.cpp @@ -101,13 +101,6 @@ RaceGUIBase::RaceGUIBase() m_referee = NULL; m_multitouch_gui = NULL; - - if (UserConfigParams::m_multitouch_enabled && - UserConfigParams::m_multitouch_mode != 0 && - race_manager->getNumLocalPlayers() == 1) - { - m_multitouch_gui = new RaceGUIMultitouch(this); - } } // RaceGUIBase // ---------------------------------------------------------------------------- @@ -173,7 +166,6 @@ RaceGUIBase::~RaceGUIBase() // If the referee is currently being shown, // remove it from the scene graph. delete m_referee; - delete m_multitouch_gui; } // ~RaceGUIBase //----------------------------------------------------------------------------- diff --git a/src/states_screens/race_gui_overworld.cpp b/src/states_screens/race_gui_overworld.cpp index 9a1ce79eb..c2d8b0099 100644 --- a/src/states_screens/race_gui_overworld.cpp +++ b/src/states_screens/race_gui_overworld.cpp @@ -86,6 +86,13 @@ RaceGUIOverworld::RaceGUIOverworld() float scaling = irr_driver->getFrameSize().Height / 420.0f; const float map_size = 250.0f; + + if (UserConfigParams::m_multitouch_enabled && + UserConfigParams::m_multitouch_mode != 0 && + race_manager->getNumLocalPlayers() == 1) + { + m_multitouch_gui = new RaceGUIMultitouch(this); + } // Check if we have enough space for minimap when touch steering is enabled if (m_multitouch_gui != NULL) @@ -156,6 +163,7 @@ RaceGUIOverworld::RaceGUIOverworld() //----------------------------------------------------------------------------- RaceGUIOverworld::~RaceGUIOverworld() { + delete m_multitouch_gui; } // ~RaceGUIOverworld //----------------------------------------------------------------------------- From 184ecce836491c3d4e671d81c5d85c67c99187e4 Mon Sep 17 00:00:00 2001 From: Deve Date: Tue, 29 Aug 2017 21:44:27 +0200 Subject: [PATCH 073/125] Reset multitouch buttons state when game is restarted --- src/input/multitouch_device.cpp | 31 ++++++++++++++++------ src/input/multitouch_device.hpp | 1 + src/states_screens/race_gui_base.cpp | 5 ++++ src/states_screens/race_gui_multitouch.cpp | 12 +++++++++ src/states_screens/race_gui_multitouch.hpp | 1 + 5 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/input/multitouch_device.cpp b/src/input/multitouch_device.cpp index cbd6f8610..9d1c8e4eb 100644 --- a/src/input/multitouch_device.cpp +++ b/src/input/multitouch_device.cpp @@ -43,14 +43,7 @@ MultitouchDevice::MultitouchDevice() assert(m_android_device != NULL); #endif - for (MultitouchEvent& event : m_events) - { - event.id = 0; - event.touched = false; - event.x = 0; - event.y = 0; - } - + reset(); updateConfigParams(); } // MultitouchDevice @@ -183,6 +176,28 @@ void MultitouchDevice::clearButtons() m_buttons.clear(); } // clearButtons +// ---------------------------------------------------------------------------- +/** Sets all buttons and events to default state + */ +void MultitouchDevice::reset() +{ + for (MultitouchButton* button : m_buttons) + { + button->pressed = false; + button->event_id = 0; + button->axis_x = 0.0f; + button->axis_y = 0.0f; + } + + for (MultitouchEvent& event : m_events) + { + event.id = 0; + event.touched = false; + event.x = 0; + event.y = 0; + } +} // reset + // ---------------------------------------------------------------------------- /** The function that is executed when touch event occurs. It updates the * buttons state when it's needed. diff --git a/src/input/multitouch_device.hpp b/src/input/multitouch_device.hpp index 47c644f22..8dd21d153 100644 --- a/src/input/multitouch_device.hpp +++ b/src/input/multitouch_device.hpp @@ -111,6 +111,7 @@ public: void addButton(MultitouchButtonType type, int x, int y, int width, int height); void clearButtons(); + void reset(); /** Returns the number of created buttons */ unsigned int getButtonsCount() {return m_buttons.size();} diff --git a/src/states_screens/race_gui_base.cpp b/src/states_screens/race_gui_base.cpp index 75b519ad7..9966b2041 100644 --- a/src/states_screens/race_gui_base.cpp +++ b/src/states_screens/race_gui_base.cpp @@ -154,6 +154,11 @@ void RaceGUIBase::reset() m_plunger_speed = core::vector2df(0,0); m_plunger_state = PLUNGER_STATE_INIT; clearAllMessages(); + + if (m_multitouch_gui != NULL) + { + m_multitouch_gui->reset(); + } } // reset //----------------------------------------------------------------------------- diff --git a/src/states_screens/race_gui_multitouch.cpp b/src/states_screens/race_gui_multitouch.cpp index 0a906eb49..84c49e3b5 100644 --- a/src/states_screens/race_gui_multitouch.cpp +++ b/src/states_screens/race_gui_multitouch.cpp @@ -76,6 +76,18 @@ RaceGUIMultitouch::~RaceGUIMultitouch() } // ~RaceGUIMultitouch +//----------------------------------------------------------------------------- +/** Sets the multitouch race gui to its initial state + */ +void RaceGUIMultitouch::reset() +{ + if (m_device != NULL) + { + m_device->reset(); + } +} // reset + + //----------------------------------------------------------------------------- /** Clears all previously created buttons in the multitouch device */ diff --git a/src/states_screens/race_gui_multitouch.hpp b/src/states_screens/race_gui_multitouch.hpp index 876551a94..c691ca078 100644 --- a/src/states_screens/race_gui_multitouch.hpp +++ b/src/states_screens/race_gui_multitouch.hpp @@ -62,6 +62,7 @@ public: unsigned int getMinimapBottom() {return m_minimap_bottom;} void setGuiAction(bool enabled = true) {m_gui_action = enabled;} + void reset(); }; // RaceGUIMultitouch From e05ef71b7760662628e220ee1b9ef0df38429d8e Mon Sep 17 00:00:00 2001 From: Deve Date: Tue, 29 Aug 2017 21:59:44 +0200 Subject: [PATCH 074/125] Make it clear that NDK >= r15b is atm. not supported --- android/README.ANDROID | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android/README.ANDROID b/android/README.ANDROID index bd8793919..689d09928 100644 --- a/android/README.ANDROID +++ b/android/README.ANDROID @@ -44,7 +44,8 @@ and extract it to stk-code/lib. It contains sources of libraries that are used in STK, but are not availiable in stk-code repository (curl, freetype, openal). You need also Android SDK for android-19 platform (the API for Android 4.4) and -Android NDK r12b. +Android NDK. Note that NDK >= r15b is atm. not supported. Version r12b is +strongly recommended, because it's known that it works without issues. You need to create proper "android-sdk" and "android-ndk" symlinks in the directory with Android project, so that the compilation script will have access From f08b53c1fcd5c172ba231126366fa10d62771b5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Allan=20Nordh=C3=B8y?= Date: Wed, 30 Aug 2017 01:47:07 +0200 Subject: [PATCH 075/125] Spelling: Chose, add-ons, low, command-line (#2914) * Spelling: Chose, add-ons, low, command-line, HTTPS * Back to http for privacy.supertuxkart.net It auto-negotiates for https from Let's Encrypt when connected to through http, strange. --- src/main.cpp | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 719da100c..4675bbe93 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1132,7 +1132,7 @@ int handleCmdLine() if(CommandLine::has("--track", &s) || CommandLine::has("-t", &s)) { race_manager->setTrack(s); - Log::verbose("main", "You choose to start in track '%s'.", + Log::verbose("main", "You chose to start in track '%s'.", s.c_str()); Track* t = track_manager->getTrack(s); @@ -1142,7 +1142,7 @@ int handleCmdLine() } else if (t->isArena()) { - //if it's arena, don't create ai karts + //if it's arena, don't create AI karts const std::vector l; race_manager->setDefaultAIKartList(l); // Add 1 for the player kart @@ -1151,7 +1151,7 @@ int handleCmdLine() } else if (t->isSoccer()) { - //if it's soccer, don't create ai karts + //if it's soccer, don't create AI karts const std::vector l; race_manager->setDefaultAIKartList(l); // Add 1 for the player kart @@ -1218,7 +1218,7 @@ int handleCmdLine() } else { - Log::verbose("main", "You choose to have %d laps.", laps); + Log::verbose("main", "You chose to have %d laps.", laps); race_manager->setNumLaps(laps); } } // --laps @@ -1330,7 +1330,7 @@ int handleCmdLine() if (request->isSuccess()) { - Log::info("Main", "Logged in from command line."); + Log::info("Main", "Logged in from command-line."); } } @@ -1390,9 +1390,9 @@ void initRest() font_manager->loadFonts(); GUIEngine::init(device, driver, StateManager::get()); - // This only initialises the non-network part of the addons manager. The - // online section of the addons manager will be initialised from a - // separate thread running in network http. + // This only initialises the non-network part of the add-ons manager. The + // online section of the add-ons manager will be initialised from a + // separate thread running in network HTTP. addons_manager = new AddonsManager(); Online::ProfileManager::create(); @@ -1477,7 +1477,7 @@ void askForInternetPermission() // Typically internet is disabled here (just better safe // than sorry). If internet should be allowed, the news // manager needs to be started (which in turn activates - // the addons manager). + // the add-ons manager). bool need_to_start_news_manager = UserConfigParams::m_internet_status != Online::RequestManager::IPERM_ALLOWED; @@ -1615,8 +1615,8 @@ int main(int argc, char *argv[] ) addons_manager->checkInstalledAddons(); - // Load addons.xml to get info about addons even when not - // allowed to access the internet + // Load addons.xml to get info about add-ons even when not + // allowed to access the Internet if (UserConfigParams::m_internet_status != Online::RequestManager::IPERM_ALLOWED) { @@ -1630,7 +1630,7 @@ int main(int argc, char *argv[] ) } catch (std::runtime_error& e) { - Log::warn("Addons", "Exception thrown when initializing addons manager : %s", e.what()); + Log::warn("Addons", "Exception thrown when initializing add-ons manager : %s", e.what()); } } } @@ -1652,7 +1652,7 @@ int main(int argc, char *argv[] ) { MessageDialog *dialog = new MessageDialog(_("Your screen resolution is too " - "small to run STK."), + "low to run STK."), /*from queue*/ true); GUIEngine::DialogQueue::get()->pushDialog(dialog); } @@ -1898,7 +1898,7 @@ static void cleanSuperTuxKart() // But still give them some additional time to finish. It avoids a // race condition where a thread might access the file manager after it // was deleted (in cleanUserConfig below), but before STK finishes and - // the os takes all threads down. + // the OS takes all threads down. if(!NewsManager::get()->waitForReadyToDeleted(2.0f)) { @@ -1918,12 +1918,12 @@ static void cleanSuperTuxKart() } SFXManager::destroy(); - // Music manager can not be deleted before the sfx thread is stopped - // (since sfx commands can contain music information, which are + // Music manager can not be deleted before the SFX thread is stopped + // (since SFX commands can contain music information, which are // deleted by the music manager). delete music_manager; - // The addons manager might still be called from a currenty running request + // The add-ons manager might still be called from a currenty running request // in the request manager, so it can not be deleted earlier. if(addons_manager) delete addons_manager; From 6e30154fc31d8745696694796d5384ec3c7e693f Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Tue, 29 Aug 2017 19:48:32 -0400 Subject: [PATCH 076/125] Update strings --- data/po/supertuxkart.pot | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/data/po/supertuxkart.pot b/data/po/supertuxkart.pot index 0444ecccb..cb8f96d6c 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: 2017-08-28 19:28-0400\n" +"POT-Creation-Date: 2017-08-29 19:48-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -868,7 +868,7 @@ msgstr "" #. I18N: ./data/gui/main_menu.stkgui #. I18N: In the main screen -#: src/states_screens/race_gui_overworld.cpp:503 +#: src/states_screens/race_gui_overworld.cpp:511 msgid "Tutorial" msgstr "" @@ -2749,7 +2749,7 @@ msgid "" msgstr "" #: src/main.cpp:1654 -msgid "Your screen resolution is too small to run STK." +msgid "Your screen resolution is too low to run STK." msgstr "" #: src/main.cpp:1668 @@ -3761,7 +3761,7 @@ msgstr "" msgid "Rendered image quality: %s" msgstr "" -#: src/states_screens/race_gui.cpp:350 src/states_screens/race_gui.cpp:352 +#: src/states_screens/race_gui.cpp:358 src/states_screens/race_gui.cpp:360 msgid "Challenge Failed" msgstr "" @@ -3786,42 +3786,42 @@ 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:520 +#: src/states_screens/race_gui_base.cpp:517 msgid "by" msgstr "" -#: src/states_screens/race_gui_base.cpp:629 +#: src/states_screens/race_gui_base.cpp:626 msgid "Collect nitro!" msgstr "" -#: src/states_screens/race_gui_base.cpp:631 +#: src/states_screens/race_gui_base.cpp:628 msgid "Follow the leader!" msgstr "" #. I18N: When some GlobalPlayerIcons are hidden, write "Top 10" to show it -#: src/states_screens/race_gui_base.cpp:809 +#: src/states_screens/race_gui_base.cpp:806 #, c-format msgid "Top %i" msgstr "" #. I18N: Shown at the end of a race -#: src/states_screens/race_gui_overworld.cpp:136 +#: src/states_screens/race_gui_overworld.cpp:143 msgid "Lap" msgstr "" -#: src/states_screens/race_gui_overworld.cpp:137 +#: src/states_screens/race_gui_overworld.cpp:144 msgid "Rank" msgstr "" -#: src/states_screens/race_gui_overworld.cpp:510 +#: src/states_screens/race_gui_overworld.cpp:518 msgid "Press fire to start the tutorial" msgstr "" -#: src/states_screens/race_gui_overworld.cpp:549 +#: src/states_screens/race_gui_overworld.cpp:557 msgid "Type: Grand Prix" msgstr "" -#: src/states_screens/race_gui_overworld.cpp:586 +#: src/states_screens/race_gui_overworld.cpp:594 msgid "Press fire to start the challenge" msgstr "" From 2de3a39e048d2317887b828b4586a1fe78c6577c Mon Sep 17 00:00:00 2001 From: deve Date: Wed, 30 Aug 2017 06:46:55 +0200 Subject: [PATCH 077/125] Make device name translatable --- src/states_screens/options_screen_input.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/states_screens/options_screen_input.cpp b/src/states_screens/options_screen_input.cpp index e8728fdd7..92ea2599c 100644 --- a/src/states_screens/options_screen_input.cpp +++ b/src/states_screens/options_screen_input.cpp @@ -134,7 +134,8 @@ void OptionsScreenInput::buildDeviceList() if (touch_device != NULL) { - devices->addItem("touch_device", " Touch device", 1); + devices->addItem("touch_device", (core::stringw(" ") + + _("Touch Device")).c_str(), 1); } } // buildDeviceList From 8c73b67309c71857fa40181c92a7b6f605e71207 Mon Sep 17 00:00:00 2001 From: deve Date: Wed, 30 Aug 2017 06:50:51 +0200 Subject: [PATCH 078/125] Update supertuxkart.pot --- data/po/supertuxkart.pot | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/data/po/supertuxkart.pot b/data/po/supertuxkart.pot index cb8f96d6c..eeaf75bb7 100644 --- a/data/po/supertuxkart.pot +++ b/data/po/supertuxkart.pot @@ -1,6 +1,6 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. +# This file is distributed under the same license as the supertuxkart package. # FIRST AUTHOR , YEAR. # #, fuzzy @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: supertuxkart\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-08-29 19:48-0400\n" +"POT-Creation-Date: 2017-08-30 06:48+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -1403,7 +1403,7 @@ msgstr "" #. I18N: ./data/gui/options_audio.stkgui #. I18N: Section in the settings menu #: src/states_screens/options_screen_device.cpp:86 -#: src/states_screens/options_screen_input.cpp:150 +#: src/states_screens/options_screen_input.cpp:151 #: src/states_screens/options_screen_ui.cpp:123 #: src/states_screens/options_screen_video.cpp:238 #: src/states_screens/user_screen.cpp:660 @@ -1479,7 +1479,7 @@ msgstr "" #: src/states_screens/ghost_replay_selection.cpp:78 #: src/states_screens/options_screen_audio.cpp:67 #: src/states_screens/options_screen_device.cpp:88 -#: src/states_screens/options_screen_input.cpp:152 +#: src/states_screens/options_screen_input.cpp:153 #: src/states_screens/options_screen_ui.cpp:124 #: src/states_screens/options_screen_video.cpp:240 #: src/states_screens/server_selection.cpp:103 @@ -1504,7 +1504,7 @@ msgstr "" #. I18N: Section in the settings menu #: src/states_screens/options_screen_audio.cpp:66 #: src/states_screens/options_screen_device.cpp:87 -#: src/states_screens/options_screen_input.cpp:151 +#: src/states_screens/options_screen_input.cpp:152 #: src/states_screens/options_screen_video.cpp:239 #: src/states_screens/user_screen.cpp:661 msgid "User Interface" @@ -1544,7 +1544,7 @@ msgstr "" #. I18N: Section in the settings menu #: src/states_screens/options_screen_audio.cpp:65 #: src/states_screens/options_screen_device.cpp:85 -#: src/states_screens/options_screen_input.cpp:149 +#: src/states_screens/options_screen_input.cpp:150 #: src/states_screens/options_screen_ui.cpp:122 #: src/states_screens/user_screen.cpp:659 msgid "Graphics" @@ -3197,7 +3197,13 @@ msgid "Username and/or email address invalid." msgstr "" #: src/states_screens/dialogs/registration_dialog.cpp:42 -msgid "Please read the terms and conditions " +#, c-format +msgid "" +"Please read the terms and conditions for SuperTuxKart at '%s'. You must " +"agree to these terms in order to register an account for STK. By checking " +"the box below, you are confirming that you understand these terms. If you " +"have any questions or comments regarding these terms, one of the members of " +"the development team would gladly assist you." msgstr "" #: src/states_screens/dialogs/select_challenge.cpp:52 @@ -3650,6 +3656,10 @@ msgstr "" msgid "Keyboard %i" msgstr "" +#: src/states_screens/options_screen_input.cpp:138 +msgid "Touch Device" +msgstr "" + #: src/states_screens/options_screen_ui.cpp:159 msgid "" "In multiplayer mode, players can select handicapped (more difficult) " From 4440c7e40a5c3b65e91d23ca398b464500b8b160 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 30 Aug 2017 17:46:07 +1000 Subject: [PATCH 079/125] Added error check for alDeleteSource. --- src/audio/sfx_openal.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/audio/sfx_openal.cpp b/src/audio/sfx_openal.cpp index 174c60db3..652fd56c6 100644 --- a/src/audio/sfx_openal.cpp +++ b/src/audio/sfx_openal.cpp @@ -70,6 +70,7 @@ SFXOpenAL::~SFXOpenAL() if (m_status!=SFX_UNKNOWN && m_status!=SFX_NOT_INITIALISED) { alDeleteSources(1, &m_sound_source); + SFXManager::checkError("deleting a source"); } if (m_owns_buffer && m_sound_buffer) From 0dd3bbbd9bfeb4b1b3e9d08eb2546267017b620e Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 30 Aug 2017 17:54:45 +1000 Subject: [PATCH 080/125] Changed severity level so that additional information in case of a crash is certain to be seen. --- src/karts/kart_model.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/karts/kart_model.cpp b/src/karts/kart_model.cpp index f3e2313f8..77296b1ff 100644 --- a/src/karts/kart_model.cpp +++ b/src/karts/kart_model.cpp @@ -904,7 +904,7 @@ void KartModel::OnAnimationEnd(scene::IAnimatedMeshSceneNode *node) if(m_current_animation==AF_DEFAULT || m_animation_frame[m_current_animation]<=-1) { - Log::debug("Kart_Model", "OnAnimationEnd for '%s': current %d frame %d", + Log::error("Kart_Model", "OnAnimationEnd for '%s': current %d frame %d", m_model_filename.c_str(), m_current_animation, m_animation_frame[m_current_animation]); assert(false); From 69e26d7561df4b8cd399c46a3ba634f76b5f32b4 Mon Sep 17 00:00:00 2001 From: Deve Date: Wed, 30 Aug 2017 21:21:43 +0200 Subject: [PATCH 081/125] Remove unclear warning. Weather node without particles is fine and it's used in Blackhill Mansion (just for lightning). The warning was suggesting that something is wrong there. --- src/tracks/track.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index 1a32ac064..b6b9fad50 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -2030,11 +2030,6 @@ void Track::loadObjects(const XMLNode* root, const std::string& path, ModelDefin m_sky_particles = ParticleKindManager::get()->getParticles(weather_particles); } - else - { - Log::error("track", "Bad weather node found - ignored.\n"); - continue; - } } else if (name == "sun") { From 64c1e8d19c018a455c3b057158383a45c46b6380 Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Wed, 30 Aug 2017 20:39:20 -0400 Subject: [PATCH 082/125] Fix SFX leaks --- src/items/powerup.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/items/powerup.cpp b/src/items/powerup.cpp index ca669e551..c1572f7fc 100644 --- a/src/items/powerup.cpp +++ b/src/items/powerup.cpp @@ -325,6 +325,11 @@ void Powerup::use() } } + if (m_sound_use != NULL) + { + m_sound_use->deleteSFX(); + m_sound_use = NULL; + } m_sound_use = SFXManager::get()->createSoundSource("inflate");//Extraordinary. Usually sounds are set in Powerup::set() //In this case this is a workaround, since the bubblegum item has two different sounds. From 4759ff24906327ba78df1a27b73c78f53c19c683 Mon Sep 17 00:00:00 2001 From: hiker Date: Thu, 31 Aug 2017 23:47:39 +1000 Subject: [PATCH 083/125] Cancel the setAnimationEndCallback when a non-existing animation is tried to be set. Fixes #2628. --- src/karts/kart_model.cpp | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/karts/kart_model.cpp b/src/karts/kart_model.cpp index 77296b1ff..c2281e5e4 100644 --- a/src/karts/kart_model.cpp +++ b/src/karts/kart_model.cpp @@ -881,6 +881,7 @@ void KartModel::setAnimation(AnimationFrameType type, bool play_non_loop) { // Special animation not found, revert to default m_current_animation = AF_DEFAULT; + m_animated_node->setAnimationEndCallback(NULL); } } // setAnimation @@ -899,17 +900,6 @@ void KartModel::OnAnimationEnd(scene::IAnimatedMeshSceneNode *node) // kart_model assert(node==m_animated_node); - // It should be a non-default type of animation, and should have - // a non negative frame (i.e. the animation is indeed defined). - if(m_current_animation==AF_DEFAULT || - m_animation_frame[m_current_animation]<=-1) - { - Log::error("Kart_Model", "OnAnimationEnd for '%s': current %d frame %d", - m_model_filename.c_str(), - m_current_animation, m_animation_frame[m_current_animation]); - assert(false); - } - // 'type' is the start frame of the animation, type + 1 the frame // to begin the loop with, type + 2 to end the frame with AnimationFrameType start = (AnimationFrameType)(m_current_animation+1); From f07a7062b3fa77d2ea092f0ee19a8b2c4dea6ce8 Mon Sep 17 00:00:00 2001 From: Benau Date: Fri, 1 Sep 2017 14:12:10 +0800 Subject: [PATCH 084/125] Add min / max height testing for all graphs --- src/tracks/arena_graph.cpp | 12 ++++++++++++ src/tracks/drive_graph.cpp | 15 ++++++++++++++- src/tracks/graph.cpp | 2 ++ src/tracks/graph.hpp | 3 +++ src/tracks/quad.cpp | 7 +++++-- src/tracks/quad.hpp | 10 ++++++++-- 6 files changed, 44 insertions(+), 5 deletions(-) diff --git a/src/tracks/arena_graph.cpp b/src/tracks/arena_graph.cpp index d888e4c47..6fa3e6ac1 100644 --- a/src/tracks/arena_graph.cpp +++ b/src/tracks/arena_graph.cpp @@ -158,6 +158,18 @@ void ArenaGraph::loadNavmesh(const std::string &navmesh) } } } + const XMLNode* ht = xml->getNode("height-testing"); + if (ht) + { + float min = Graph::MIN_HEIGHT_TESTING; + float max = Graph::MAX_HEIGHT_TESTING; + ht->get("min", &min); + ht->get("max", &max); + for (unsigned i = 0; i < m_all_nodes.size(); i++) + { + m_all_nodes[i]->setHeightTesting(min, max); + } + } delete xml; } // loadNavmesh diff --git a/src/tracks/drive_graph.cpp b/src/tracks/drive_graph.cpp index e73b730fb..8155b2ab0 100644 --- a/src/tracks/drive_graph.cpp +++ b/src/tracks/drive_graph.cpp @@ -97,16 +97,24 @@ void DriveGraph::load(const std::string &quad_file_name, return; } + float min_height_testing = Graph::MIN_HEIGHT_TESTING; + float max_height_testing = Graph::MAX_HEIGHT_TESTING; // Each quad is part of the graph exactly once now. 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" || xml_node->getName() == "height-testing")) { Log::warn("DriveGraph: Unsupported node type '%s' found in '%s' - ignored.", xml_node->getName().c_str(), filename.c_str()); continue; } + if (xml_node->getName() == "height-testing") + { + xml_node->get("min", &min_height_testing); + xml_node->get("max", &max_height_testing); + 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 @@ -141,6 +149,11 @@ void DriveGraph::load(const std::string &quad_file_name, createQuad(p0, p1, p2, p3, m_all_nodes.size(), invisible, ai_ignore, false/*is_arena*/, ignored); } + for (unsigned i = 0; i < m_all_nodes.size(); i++) + { + m_all_nodes[i]->setHeightTesting(min_height_testing, + max_height_testing); + } delete quad; const XMLNode *xml = file_manager->createXMLTree(filename); diff --git a/src/tracks/graph.cpp b/src/tracks/graph.cpp index 117da8e72..704e78c0b 100644 --- a/src/tracks/graph.cpp +++ b/src/tracks/graph.cpp @@ -31,6 +31,8 @@ #include "utils/log.hpp" const int Graph::UNKNOWN_SECTOR = -1; +const float Graph::MIN_HEIGHT_TESTING = -1.0f; +const float Graph::MAX_HEIGHT_TESTING = 5.0f; Graph *Graph::m_graph = NULL; // ----------------------------------------------------------------------------- Graph::Graph() diff --git a/src/tracks/graph.hpp b/src/tracks/graph.hpp index 31ab048fb..54de20be0 100644 --- a/src/tracks/graph.hpp +++ b/src/tracks/graph.hpp @@ -103,6 +103,9 @@ private: public: static const int UNKNOWN_SECTOR; + // For 2d Quad + static const float MIN_HEIGHT_TESTING; + static const float MAX_HEIGHT_TESTING; // ------------------------------------------------------------------------ /** Returns the one instance of this object. It is possible that there * is no instance created (e.g. arena without navmesh) so we don't assert diff --git a/src/tracks/quad.cpp b/src/tracks/quad.cpp index 48b66035f..a8c4c9f30 100644 --- a/src/tracks/quad.cpp +++ b/src/tracks/quad.cpp @@ -17,6 +17,7 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "tracks/quad.hpp" +#include "tracks/graph.hpp" #include "utils/log.hpp" #include @@ -36,6 +37,8 @@ Quad::Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, std::min(p2.getY(), p3.getY()) ); m_max_height = std::max ( std::max(p0.getY(), p1.getY()), std::max(p2.getY(), p3.getY()) ); + m_min_height_testing = Graph::MIN_HEIGHT_TESTING; + m_max_height_testing = Graph::MAX_HEIGHT_TESTING; } // Quad // ---------------------------------------------------------------------------- @@ -77,8 +80,8 @@ bool Quad::pointInside(const Vec3& p, bool ignore_vertical) const // with the minimum height of the quad (and not with the actual // height of the quad at the point where the kart is). if(!ignore_vertical && - (p.getY() - m_max_height > 5.0f || - p.getY() - m_min_height < -1.0f )) + (p.getY() - m_max_height > m_max_height_testing || + p.getY() - m_min_height < m_min_height_testing )) return false; // If a point is exactly on the line of two quads (e.g. between points diff --git a/src/tracks/quad.hpp b/src/tracks/quad.hpp index 2f046d3b3..396f618cf 100644 --- a/src/tracks/quad.hpp +++ b/src/tracks/quad.hpp @@ -58,11 +58,11 @@ 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; + float m_min_height, m_min_height_testing; /** The maximum height of the quad, used together with m_min_height * to distinguish between quads which are on top of each other. */ - float m_max_height; + float m_max_height, m_max_height_testing; public: LEAK_CHECK() @@ -81,6 +81,12 @@ public: /** Returns the center of a quad. */ const Vec3& getCenter () const { return m_center; } // ------------------------------------------------------------------------ + void setHeightTesting(float min, float max) + { + m_min_height_testing = min; + m_max_height_testing = max; + } + // ------------------------------------------------------------------------ /** Returns the minimum height of a quad. */ float getMinHeight() const { return m_min_height; } // ------------------------------------------------------------------------ From 6f11a1669f96c3082b0adf673a1f5c735ba23923 Mon Sep 17 00:00:00 2001 From: Deve Date: Sat, 2 Sep 2017 01:01:35 +0200 Subject: [PATCH 085/125] Make particles in GLES renderer to look a bit better. Now kart exhaust is not too dark anymore. --- data/shaders/flipparticle.vert | 4 ++-- data/shaders/particle.frag | 5 ++--- data/shaders/particle.vert | 7 +++++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/data/shaders/flipparticle.vert b/data/shaders/flipparticle.vert index 4c81d8082..dc2e8f5dd 100644 --- a/data/shaders/flipparticle.vert +++ b/data/shaders/flipparticle.vert @@ -22,7 +22,7 @@ float anglespeed; out float lf; out vec2 tc; -out vec3 pc; +out vec4 pc; void main(void) { @@ -68,5 +68,5 @@ void main(void) vec4 viewpos = ViewMatrix * vec4(newposition + newquadcorner, 1.0); gl_Position = ProjectionMatrix * viewpos; - pc = vec3(1.); + pc = vec4(1.); } diff --git a/data/shaders/particle.frag b/data/shaders/particle.frag index 3a2dd232a..25eec19e2 100644 --- a/data/shaders/particle.frag +++ b/data/shaders/particle.frag @@ -4,7 +4,7 @@ uniform mat4 invproj; in float lf; in vec2 tc; -in vec3 pc; +in vec4 pc; out vec4 FragColor; #stk_include "utils/getPosFromUVDepth.frag" @@ -17,6 +17,5 @@ void main(void) float EnvZ = texture(dtex, xy).x; vec4 EnvPos = getPosFromUVDepth(vec3(xy, EnvZ), InverseProjectionMatrix); float alpha = clamp((EnvPos.z - FragmentPos.z) * 0.3, 0., 1.); - vec4 color = texture(tex, tc) * vec4(pc, 1.0); - FragColor = color * alpha * smoothstep(1., 0.8, lf); + FragColor = texture(tex, tc) * pc * alpha; } diff --git a/data/shaders/particle.vert b/data/shaders/particle.vert index d9a7d8659..600e247a2 100644 --- a/data/shaders/particle.vert +++ b/data/shaders/particle.vert @@ -19,13 +19,16 @@ in vec2 quadcorner; out float lf; out vec2 tc; -out vec3 pc; +out vec4 pc; void main(void) { tc = Texcoord; lf = lifetime; - pc = color_from + (color_to - color_from) * lifetime; + pc = vec4(vec3(color_from + (color_to - color_from) * lf), 1.0) * smoothstep(1., 0.8, lf); +#if defined(GL_ES) && !defined(Advanced_Lighting_Enabled) + pc.rgb = pow(pc.rgb, vec3(1. / 2.2)); +#endif vec3 newposition = Position; vec4 viewpos = ViewMatrix * vec4(newposition, 1.0); From 6710fc2a5c1c68f58d4f2ec311bf5561b243d828 Mon Sep 17 00:00:00 2001 From: Deve Date: Sat, 2 Sep 2017 23:47:25 +0200 Subject: [PATCH 086/125] Don't add absolute path if screenshot name is empty --- src/tracks/track.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index b6b9fad50..1b2b5f51d 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -573,7 +573,10 @@ void Track::loadTrackInfo() if(xml_node) loadCurves(*xml_node); // Set the correct paths - m_screenshot = m_root+m_screenshot; + if (m_screenshot.length() > 0) + { + m_screenshot = m_root+m_screenshot; + } delete root; std::string dir = StringUtils::getPath(m_filename); From 1123ceeceb5df639ea248c2e364c17e08f538ea3 Mon Sep 17 00:00:00 2001 From: Deve Date: Sun, 3 Sep 2017 00:04:29 +0200 Subject: [PATCH 087/125] Don't try to create textures for empty names. It's not designed to work with empty file names, because the "existFile" function in irrlicht returns true for directories, our file manager doesn't check that it's a directory and generally strange things happen. This solves a problem with luna track that doesn't have a screenshot and possibly with some broken add-ons, so that valgrind doesn't complain anymore. Benau, feel free to revert it if you have better solution. --- src/graphics/stk_tex_manager.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/graphics/stk_tex_manager.cpp b/src/graphics/stk_tex_manager.cpp index 4ea5ccd94..e1b7ede45 100644 --- a/src/graphics/stk_tex_manager.cpp +++ b/src/graphics/stk_tex_manager.cpp @@ -147,6 +147,12 @@ video::ITexture* STKTexManager::getTexture(const std::string& path, TexConfig* tc, bool no_upload, bool create_if_unfound) { + if (path.empty()) + { + Log::error("STKTexManager", "Texture name is empty."); + return NULL; + } + auto ret = m_all_textures.find(path); if (!no_upload && ret != m_all_textures.end()) return ret->second; From 590ec78640b29dd5cbf59737c842af43a9652684 Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Sat, 2 Sep 2017 21:54:02 -0400 Subject: [PATCH 088/125] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63bc7e02b..64b9c875a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * New smoother camera by Auria * New grand prix win scene * Gamepad configuration bugfixes +* 3 Strikes Battles : added spare tire karts * Various improvements (wall driving fixes, parachutes, GP points, cannon fixes, colorization shader) ## SuperTuxKart 0.9.2 From 663c7aacaf62586785c14602888c67ac95adfb09 Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Sat, 2 Sep 2017 22:00:37 -0400 Subject: [PATCH 089/125] Tweak kart audio code to use fewer OpenAL sources (see #2921). Seems to work well, though I need to take a closer look to make it thread-safe --- src/audio/sfx_base.hpp | 1 + src/audio/sfx_manager.cpp | 16 +++++++ src/audio/sfx_manager.hpp | 3 ++ src/audio/sfx_openal.cpp | 29 +++++++++++++ src/audio/sfx_openal.hpp | 4 ++ src/karts/kart.cpp | 90 ++++++++++++++++++++++----------------- src/karts/kart.hpp | 17 +++++--- 7 files changed, 114 insertions(+), 46 deletions(-) diff --git a/src/audio/sfx_base.hpp b/src/audio/sfx_base.hpp index f01479346..e0fff346d 100644 --- a/src/audio/sfx_base.hpp +++ b/src/audio/sfx_base.hpp @@ -83,6 +83,7 @@ public: virtual void onSoundEnabledBack() = 0; virtual void setRolloff(float rolloff) = 0; virtual const SFXBuffer* getBuffer() const = 0; + virtual void setBuffer(SFXBuffer* buffer, bool owns_buffer) = 0; virtual SFXStatus getStatus() = 0; }; // SFXBase diff --git a/src/audio/sfx_manager.cpp b/src/audio/sfx_manager.cpp index 4761e2c47..12b24ba5d 100644 --- a/src/audio/sfx_manager.cpp +++ b/src/audio/sfx_manager.cpp @@ -675,6 +675,22 @@ SFXBase* SFXManager::createSoundSource(const std::string &name, return createSoundSource( i->second, add_to_SFXList ); } // createSoundSource +//---------------------------------------------------------------------------- + +SFXBuffer* SFXManager::getBuffer(const std::string &name) +{ + std::map::iterator i = m_all_sfx_types.find(name); + if (i == m_all_sfx_types.end()) + { + Log::error("SFXManager", + "SFXManager::getBuffer could not find the " + "requested sound effect : '%s'.", name.c_str()); + return NULL; + } + + return i->second; +} + //---------------------------------------------------------------------------- /** Returns true if a sfx with the given name exists. * \param name The internal name of the sfx (not the name of the ogg file) diff --git a/src/audio/sfx_manager.hpp b/src/audio/sfx_manager.hpp index d9bfbf515..8e0c701cc 100644 --- a/src/audio/sfx_manager.hpp +++ b/src/audio/sfx_manager.hpp @@ -296,6 +296,9 @@ public: /** Returns the current position of the listener. */ Vec3 getListenerPos() const { return m_listener_position.getData(); } + // ------------------------------------------------------------------------ + + SFXBuffer* getBuffer(const std::string &name); }; #endif // HEADER_SFX_MANAGER_HPP diff --git a/src/audio/sfx_openal.cpp b/src/audio/sfx_openal.cpp index 652fd56c6..61456e5a3 100644 --- a/src/audio/sfx_openal.cpp +++ b/src/audio/sfx_openal.cpp @@ -52,6 +52,7 @@ SFXOpenAL::SFXOpenAL(SFXBuffer* buffer, bool positional, float volume, m_master_gain = 1.0f; m_owns_buffer = owns_buffer; m_play_time = 0.0f; + m_sound_buffer_changed = false; // Don't initialise anything else if the sfx manager was not correctly // initialised. First of all the initialisation will not work, and it @@ -377,6 +378,18 @@ void SFXOpenAL::reallyPlayNow() if (m_status==SFX_UNKNOWN) return; } + if (m_sound_buffer_changed) + { + if (m_status == SFX_PLAYING || m_status == SFX_PAUSED) + reallyStopNow(); + + alSourcei(m_sound_source, AL_BUFFER, m_sound_buffer->getBufferID()); + m_sound_buffer_changed = false; + + if (!SFXManager::checkError("attaching the buffer to the source")) + return; + } + alSourcePlay(m_sound_source); SFXManager::checkError("playing"); // Esp. with terrain sounds it can (very likely) happen that the status @@ -528,4 +541,20 @@ void SFXOpenAL::setRolloff(float rolloff) alSourcef (m_sound_source, AL_ROLLOFF_FACTOR, rolloff); } +//----------------------------------------------------------------------------- + +// TODO: make this thread-safe +void SFXOpenAL::setBuffer(SFXBuffer* buffer, bool owns_buffer) +{ + if (m_owns_buffer && m_sound_buffer) + { + m_sound_buffer->unload(); + delete m_sound_buffer; + } + + m_sound_buffer = buffer; + m_owns_buffer = owns_buffer; + m_sound_buffer_changed = true; +} + #endif //if HAVE_OGGVORBIS diff --git a/src/audio/sfx_openal.hpp b/src/audio/sfx_openal.hpp index 10b1c0717..cd2d095b4 100644 --- a/src/audio/sfx_openal.hpp +++ b/src/audio/sfx_openal.hpp @@ -75,6 +75,8 @@ private: /** How long the sfx has been playing. */ float m_play_time; + bool m_sound_buffer_changed; + public: SFXOpenAL(SFXBuffer* buffer, bool positional, float volume, bool owns_buffer = false); @@ -117,6 +119,8 @@ public: // ------------------------------------------------------------------------ /** Returns the buffer associated with this sfx. */ virtual const SFXBuffer* getBuffer() const { return m_sound_buffer; } + // ------------------------------------------------------------------------ + virtual void setBuffer(SFXBuffer* buffer, bool owns_buffer); }; // SFXOpenAL diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 6379915c9..28a1c0855 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -171,12 +171,9 @@ Kart::Kart (const std::string& ident, unsigned int world_kart_id, }*/ m_engine_sound = SFXManager::get()->createSoundSource(m_kart_properties->getEngineSfxType()); - m_beep_sound = SFXManager::get()->createSoundSource( "horn" ); - m_crash_sound = SFXManager::get()->createSoundSource( "crash" ); - m_crash_sound2 = SFXManager::get()->createSoundSource( "crash2"); - m_crash_sound3 = SFXManager::get()->createSoundSource( "crash3"); - m_boing_sound = SFXManager::get()->createSoundSource( "boing" ); - m_goo_sound = SFXManager::get()->createSoundSource( "goo" ); + m_emitter_1 = SFXManager::get()->createSoundSource("crash"); + m_emitter_2 = SFXManager::get()->createSoundSource("crash"); + m_emitter_3 = SFXManager::get()->createSoundSource("crash"); m_skid_sound = SFXManager::get()->createSoundSource( "skid" ); m_nitro_sound = SFXManager::get()->createSoundSource( "nitro" ); m_terrain_sound = NULL; @@ -201,13 +198,10 @@ void Kart::init(RaceManager::KartType type) if (type == RaceManager::KT_PLAYER) factor = std::min(1.0f, race_manager->getNumLocalPlayers()/2.0f); - m_goo_sound->setVolume(factor); + m_emitter_1->setVolume(factor); + m_emitter_2->setVolume(factor); + m_emitter_3->setVolume(factor); m_skid_sound->setVolume(factor); - m_crash_sound->setVolume(factor); - m_crash_sound2->setVolume(factor); - m_crash_sound3->setVolume(factor); - m_boing_sound->setVolume(factor); - m_beep_sound->setVolume(factor); m_nitro_sound->setVolume(factor); } // if getNumLocalPlayers > 1 @@ -257,13 +251,10 @@ Kart::~Kart() }*/ m_engine_sound->deleteSFX(); - m_crash_sound ->deleteSFX(); - m_crash_sound2->deleteSFX(); - m_crash_sound3->deleteSFX(); m_skid_sound ->deleteSFX(); - m_goo_sound ->deleteSFX(); - m_beep_sound ->deleteSFX(); - m_boing_sound ->deleteSFX(); + m_emitter_1->deleteSFX(); + m_emitter_2->deleteSFX(); + m_emitter_3->deleteSFX(); m_nitro_sound ->deleteSFX(); delete m_kart_gfx; if(m_terrain_sound) m_terrain_sound->deleteSFX(); @@ -1029,8 +1020,11 @@ void Kart::collectedItem(Item *item, int add_info) m_kart_properties->getBubblegumSpeedFraction() , m_kart_properties->getBubblegumFadeInTime(), m_bubblegum_time); - m_goo_sound->setPosition(getXYZ()); - m_goo_sound->play(); + { + SFXBase* emitter = getNextEmitter(); + emitter->setBuffer(SFXManager::get()->getBuffer("goo"), false); + emitter->play(getXYZ()); + } // Play appropriate custom character sound playCustomSFX(SFXManager::CUSTOM_GOO); break; @@ -1361,12 +1355,10 @@ void Kart::update(float dt) } */ - m_beep_sound->setPosition ( getXYZ() ); - m_crash_sound->setPosition ( getXYZ() ); - m_crash_sound2->setPosition ( getXYZ() ); - m_crash_sound3->setPosition ( getXYZ() ); + m_emitter_1->setPosition(getXYZ()); + m_emitter_2->setPosition(getXYZ()); + m_emitter_3->setPosition(getXYZ()); m_skid_sound->setPosition ( getXYZ() ); - m_boing_sound->setPosition ( getXYZ() ); m_nitro_sound->setPosition ( getXYZ() ); // Check if a kart is (nearly) upside down and not moving much --> @@ -2166,23 +2158,22 @@ void Kart::playCrashSFX(const Material* m, AbstractKart *k) // it's not already playing. if (isShielded() || (k != NULL && k->isShielded())) { - if (m_boing_sound->getStatus() != SFXBase::SFX_PLAYING) - m_boing_sound->play(getXYZ()); + SFXBase* emitter = getNextEmitter(); + emitter->setBuffer(SFXManager::get()->getBuffer("boing"), false); + emitter->play(getXYZ()); } else { - if (m_crash_sound->getStatus() != SFXBase::SFX_PLAYING - && m_crash_sound2->getStatus() != SFXBase::SFX_PLAYING - && m_crash_sound3->getStatus() != SFXBase::SFX_PLAYING) - { - int idx = rand() % 3; - if (idx == 0) - m_crash_sound->play(getXYZ()); - else if (idx == 1) - m_crash_sound2->play(getXYZ()); - else - m_crash_sound3->play(getXYZ()); - } + int idx = rand() % 3; + + SFXBase* emitter = getNextEmitter(); + if (idx == 0) + emitter->setBuffer(SFXManager::get()->getBuffer("crash"), false); + else if (idx == 1) + emitter->setBuffer(SFXManager::get()->getBuffer("crash2"), false); + else + emitter->setBuffer(SFXManager::get()->getBuffer("crash3"), false); + emitter->play(getXYZ()); } } // if lin_vel > 0.555 } // if m_bounce_back_time <= 0 @@ -2196,7 +2187,9 @@ void Kart::beep() // If the custom horn can't play (isn't defined) then play the default one if (!playCustomSFX(SFXManager::CUSTOM_HORN)) { - m_beep_sound->play(getXYZ()); + SFXBase* emitter = getNextEmitter(); + emitter->setBuffer(SFXManager::get()->getBuffer("horn"), false); + emitter->play(getXYZ()); } } // beep @@ -2690,6 +2683,23 @@ void Kart::kartIsInRestNow() m_kart_model->setDefaultSuspension(); } // kartIsInRestNow +//----------------------------------------------------------------------------- + +SFXBase* Kart::getNextEmitter() +{ + m_emitter_id = (m_emitter_id + 1) % 3; + + SFXBase* next_emitter; + if (m_emitter_id == 0) + next_emitter = m_emitter_1; + else if (m_emitter_id == 1) + next_emitter = m_emitter_2; + else + next_emitter = m_emitter_3; + + return next_emitter; +} + //----------------------------------------------------------------------------- /** Updates the graphics model. It is responsible for positioning the graphical * chasses at an 'appropriate' position: typically, the physical model has diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index 9440130f1..3e2f7a307 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -206,19 +206,23 @@ protected: float m_last_factor_engine_sound; std::vector m_custom_sounds; - SFXBase *m_beep_sound; + int m_emitter_id = 0; + SFXBase *m_emitter_1; + SFXBase *m_emitter_2; + SFXBase *m_emitter_3; + //SFXBase *m_beep_sound; SFXBase *m_engine_sound; - SFXBase *m_crash_sound; - SFXBase *m_crash_sound2; - SFXBase *m_crash_sound3; + //SFXBase *m_crash_sound; + //SFXBase *m_crash_sound2; + //SFXBase *m_crash_sound3; SFXBase *m_terrain_sound; SFXBase *m_nitro_sound; /** A pointer to the previous terrain sound needs to be saved so that an * 'older' sfx can be finished and an abrupt end of the sfx is avoided. */ SFXBase *m_previous_terrain_sound; SFXBase *m_skid_sound; - SFXBase *m_goo_sound; - SFXBase *m_boing_sound; + //SFXBase *m_goo_sound; + //SFXBase *m_boing_sound; float m_time_last_crash; RaceManager::KartType m_type; @@ -237,6 +241,7 @@ protected: float getActualWheelForce(); void playCrashSFX(const Material* m, AbstractKart *k); void loadData(RaceManager::KartType type, bool animatedModel); + SFXBase* getNextEmitter(); public: Kart(const std::string& ident, unsigned int world_kart_id, From cb2295c7383863d1bece46524291d163154cea1a Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Sat, 2 Sep 2017 22:35:41 -0400 Subject: [PATCH 090/125] Improve kart audio refactor, see #2921 --- src/audio/dummy_sfx.hpp | 6 +++--- src/audio/sfx_base.hpp | 7 +++---- src/audio/sfx_manager.cpp | 11 ++++++++++- src/audio/sfx_manager.hpp | 17 +++++++++++------ src/audio/sfx_openal.cpp | 33 ++++++++++----------------------- src/audio/sfx_openal.hpp | 15 ++++++--------- src/karts/kart.cpp | 34 +++++++++++++++++----------------- src/karts/kart.hpp | 12 ++++++------ 8 files changed, 66 insertions(+), 69 deletions(-) diff --git a/src/audio/dummy_sfx.hpp b/src/audio/dummy_sfx.hpp index 63f9893e9..32eb1116d 100644 --- a/src/audio/dummy_sfx.hpp +++ b/src/audio/dummy_sfx.hpp @@ -47,9 +47,9 @@ public: virtual void reallySetSpeedPosition(float f, const Vec3 &p) {} virtual void play() {} - virtual void reallyPlayNow() {} - virtual void play(const Vec3 &xyz) {} - virtual void reallyPlayNow(const Vec3 &xyz) {} + virtual void reallyPlayNow(SFXBuffer* buffer = NULL) {} + virtual void play(const Vec3 &xyz, SFXBuffer* buffer = NULL) {} + virtual void reallyPlayNow(const Vec3 &xyz, SFXBuffer* buffer = NULL) {} virtual void stop() {} virtual void reallyStopNow() {} virtual void pause() {} diff --git a/src/audio/sfx_base.hpp b/src/audio/sfx_base.hpp index e0fff346d..da601bbf1 100644 --- a/src/audio/sfx_base.hpp +++ b/src/audio/sfx_base.hpp @@ -64,9 +64,9 @@ public: virtual void setLoop(bool status) = 0; virtual void reallySetLoop(bool status) = 0; virtual void play() = 0; - virtual void reallyPlayNow() = 0; - virtual void play(const Vec3 &xyz) = 0; - virtual void reallyPlayNow(const Vec3 &xyz) = 0; + virtual void reallyPlayNow(SFXBuffer* buffer = NULL) = 0; + virtual void play(const Vec3 &xyz, SFXBuffer* buffer = NULL) = 0; + virtual void reallyPlayNow(const Vec3 &xyz, SFXBuffer* buffer = NULL) = 0; virtual void stop() = 0; virtual void reallyStopNow() = 0; virtual void pause() = 0; @@ -83,7 +83,6 @@ public: virtual void onSoundEnabledBack() = 0; virtual void setRolloff(float rolloff) = 0; virtual const SFXBuffer* getBuffer() const = 0; - virtual void setBuffer(SFXBuffer* buffer, bool owns_buffer) = 0; virtual SFXStatus getStatus() = 0; }; // SFXBase diff --git a/src/audio/sfx_manager.cpp b/src/audio/sfx_manager.cpp index 12b24ba5d..9cecda9dc 100644 --- a/src/audio/sfx_manager.cpp +++ b/src/audio/sfx_manager.cpp @@ -217,6 +217,15 @@ void SFXManager::queue(SFXCommands command, SFXBase *sfx, const Vec3 &p) queueCommand(sfx_command); } // queue (Vec3) +//---------------------------------------------------------------------------- + +void SFXManager::queue(SFXCommands command, SFXBase *sfx, const Vec3 &p, SFXBuffer* buffer) +{ + SFXCommand *sfx_command = new SFXCommand(command, sfx, p); + sfx_command->m_buffer = buffer; + queueCommand(sfx_command); +} // queue (Vec3) + //---------------------------------------------------------------------------- /** Adds a sound effect command with a float and a Vec3 parameter to the queue * of the sfx manager. Openal commands can sometimes cause a 5ms delay, so it @@ -340,7 +349,7 @@ void* SFXManager::mainLoop(void *obj) { case SFX_PLAY: current->m_sfx->reallyPlayNow(); break; case SFX_PLAY_POSITION: - current->m_sfx->reallyPlayNow(current->m_parameter); break; + current->m_sfx->reallyPlayNow(current->m_parameter, current->m_buffer); break; case SFX_STOP: current->m_sfx->reallyStopNow(); break; case SFX_PAUSE: current->m_sfx->reallyPauseNow(); break; case SFX_RESUME: current->m_sfx->reallyResumeNow(); break; diff --git a/src/audio/sfx_manager.hpp b/src/audio/sfx_manager.hpp index 8e0c701cc..beb476395 100644 --- a/src/audio/sfx_manager.hpp +++ b/src/audio/sfx_manager.hpp @@ -122,6 +122,9 @@ private: /** The sound effect for which the command should be executed. */ SFXBase *m_sfx; + /** The sound buffer to play (null = no change) */ + SFXBuffer *m_buffer = NULL; + /** Stores music information for music commands. */ MusicInformation *m_music_information; @@ -232,12 +235,14 @@ private: public: static void create(); static void destroy(); - void queue(SFXCommands command, SFXBase *sfx=NULL); - void queue(SFXCommands command, SFXBase *sfx, float f); - void queue(SFXCommands command, SFXBase *sfx, const Vec3 &p); - void queue(SFXCommands command, SFXBase *sfx, float f, const Vec3 &p); - void queue(SFXCommands command, MusicInformation *mi); - void queue(SFXCommands command, MusicInformation *mi, float f); + void queue(SFXCommands command, SFXBase *sfx=NULL); + void queue(SFXCommands command, SFXBase *sfx, float f); + void queue(SFXCommands command, SFXBase *sfx, const Vec3 &p); + void queue(SFXCommands command, SFXBase *sfx, float f, const Vec3 &p); + void queue(SFXCommands command, MusicInformation *mi); + void queue(SFXCommands command, MusicInformation *mi, float f); + void queue(SFXCommands command, SFXBase *sfx, const Vec3 &p, SFXBuffer* buffer); + // ------------------------------------------------------------------------ /** Static function to get the singleton sfx manager. */ static SFXManager *get() diff --git a/src/audio/sfx_openal.cpp b/src/audio/sfx_openal.cpp index 61456e5a3..21368e1b5 100644 --- a/src/audio/sfx_openal.cpp +++ b/src/audio/sfx_openal.cpp @@ -52,7 +52,6 @@ SFXOpenAL::SFXOpenAL(SFXBuffer* buffer, bool positional, float volume, m_master_gain = 1.0f; m_owns_buffer = owns_buffer; m_play_time = 0.0f; - m_sound_buffer_changed = false; // Don't initialise anything else if the sfx manager was not correctly // initialised. First of all the initialisation will not work, and it @@ -366,7 +365,7 @@ void SFXOpenAL::play() //----------------------------------------------------------------------------- /** Plays this sound effect. */ -void SFXOpenAL::reallyPlayNow() +void SFXOpenAL::reallyPlayNow(SFXBuffer* buffer) { if (!SFXManager::get()->sfxAllowed()) return; if (m_status==SFX_NOT_INITIALISED) @@ -378,13 +377,13 @@ void SFXOpenAL::reallyPlayNow() if (m_status==SFX_UNKNOWN) return; } - if (m_sound_buffer_changed) + if (buffer != NULL) { if (m_status == SFX_PLAYING || m_status == SFX_PAUSED) reallyStopNow(); + m_sound_buffer = buffer; alSourcei(m_sound_source, AL_BUFFER, m_sound_buffer->getBufferID()); - m_sound_buffer_changed = false; if (!SFXManager::checkError("attaching the buffer to the source")) return; @@ -405,8 +404,11 @@ void SFXOpenAL::reallyPlayNow() /** This actually queues up the sfx in the sfx manager. It will be started * from a separate thread later (in this frame). */ -void SFXOpenAL::play(const Vec3 &position) +void SFXOpenAL::play(const Vec3 &position, SFXBuffer* buffer) { + if (m_owns_buffer && buffer != NULL) + assert(false); // sources that own a buffer cannot play any other buffer + if (m_status == SFX_UNKNOWN || !SFXManager::get()->sfxAllowed()) return; if(m_status==SFX_STOPPED || m_status==SFX_NOT_INITIALISED) @@ -418,16 +420,15 @@ void SFXOpenAL::play(const Vec3 &position) // - which can happen if the sfx thread had no time to actually start // it yet. m_status = SFX_PLAYING; - SFXManager::get()->queue(SFXManager::SFX_PLAY_POSITION, this, position); + SFXManager::get()->queue(SFXManager::SFX_PLAY_POSITION, this, position, buffer); } // play(Vec3) - //----------------------------------------------------------------------------- /** Plays this sound effect. */ -void SFXOpenAL::reallyPlayNow(const Vec3 &position) +void SFXOpenAL::reallyPlayNow(const Vec3 &position, SFXBuffer* buffer) { reallySetPosition(position); - reallyPlayNow(); + reallyPlayNow(buffer); } // reallyPlayNow(Vec3) //----------------------------------------------------------------------------- @@ -543,18 +544,4 @@ void SFXOpenAL::setRolloff(float rolloff) //----------------------------------------------------------------------------- -// TODO: make this thread-safe -void SFXOpenAL::setBuffer(SFXBuffer* buffer, bool owns_buffer) -{ - if (m_owns_buffer && m_sound_buffer) - { - m_sound_buffer->unload(); - delete m_sound_buffer; - } - - m_sound_buffer = buffer; - m_owns_buffer = owns_buffer; - m_sound_buffer_changed = true; -} - #endif //if HAVE_OGGVORBIS diff --git a/src/audio/sfx_openal.hpp b/src/audio/sfx_openal.hpp index cd2d095b4..0d491b097 100644 --- a/src/audio/sfx_openal.hpp +++ b/src/audio/sfx_openal.hpp @@ -29,6 +29,7 @@ #endif #include "audio/sfx_base.hpp" #include "utils/leak_check.hpp" +#include "utils/cpp2011.hpp" /** * \brief OpenAL implementation of the abstract SFXBase interface @@ -75,19 +76,17 @@ private: /** How long the sfx has been playing. */ float m_play_time; - bool m_sound_buffer_changed; - public: SFXOpenAL(SFXBuffer* buffer, bool positional, float volume, bool owns_buffer = false); virtual ~SFXOpenAL(); virtual void updatePlayingSFX(float dt); - virtual bool init(); - virtual void play(); - virtual void reallyPlayNow(); - virtual void play(const Vec3 &xyz); - virtual void reallyPlayNow(const Vec3 &xyz); + virtual bool init() OVERRIDE; + virtual void play() OVERRIDE; + virtual void reallyPlayNow(SFXBuffer* buffer = NULL) OVERRIDE; + virtual void play(const Vec3 &xyz, SFXBuffer* buffer = NULL) OVERRIDE; + virtual void reallyPlayNow(const Vec3 &xyz, SFXBuffer* buffer = NULL) OVERRIDE; virtual void setLoop(bool status); virtual void reallySetLoop(bool status); virtual void stop(); @@ -119,8 +118,6 @@ public: // ------------------------------------------------------------------------ /** Returns the buffer associated with this sfx. */ virtual const SFXBuffer* getBuffer() const { return m_sound_buffer; } - // ------------------------------------------------------------------------ - virtual void setBuffer(SFXBuffer* buffer, bool owns_buffer); }; // SFXOpenAL diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 28a1c0855..5e20d8a67 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -170,6 +170,13 @@ Kart::Kart (const std::string& ident, unsigned int world_kart_id, } }*/ + m_horn_sound = SFXManager::get()->getBuffer("horn"); + m_crash_sound = SFXManager::get()->getBuffer("crash"); + m_crash_sound2 = SFXManager::get()->getBuffer("crash2"); + m_crash_sound3 = SFXManager::get()->getBuffer("crash3"); + m_goo_sound = SFXManager::get()->getBuffer("goo"); + m_boing_sound = SFXManager::get()->getBuffer("boing"); + m_engine_sound = SFXManager::get()->createSoundSource(m_kart_properties->getEngineSfxType()); m_emitter_1 = SFXManager::get()->createSoundSource("crash"); m_emitter_2 = SFXManager::get()->createSoundSource("crash"); @@ -1020,11 +1027,8 @@ void Kart::collectedItem(Item *item, int add_info) m_kart_properties->getBubblegumSpeedFraction() , m_kart_properties->getBubblegumFadeInTime(), m_bubblegum_time); - { - SFXBase* emitter = getNextEmitter(); - emitter->setBuffer(SFXManager::get()->getBuffer("goo"), false); - emitter->play(getXYZ()); - } + getNextEmitter()->play(getXYZ(), m_goo_sound); + // Play appropriate custom character sound playCustomSFX(SFXManager::CUSTOM_GOO); break; @@ -2158,22 +2162,20 @@ void Kart::playCrashSFX(const Material* m, AbstractKart *k) // it's not already playing. if (isShielded() || (k != NULL && k->isShielded())) { - SFXBase* emitter = getNextEmitter(); - emitter->setBuffer(SFXManager::get()->getBuffer("boing"), false); - emitter->play(getXYZ()); + getNextEmitter()->play(getXYZ(), m_boing_sound); } else { int idx = rand() % 3; - - SFXBase* emitter = getNextEmitter(); + SFXBuffer* buffer; if (idx == 0) - emitter->setBuffer(SFXManager::get()->getBuffer("crash"), false); + buffer = m_crash_sound; else if (idx == 1) - emitter->setBuffer(SFXManager::get()->getBuffer("crash2"), false); + buffer = m_crash_sound2; else - emitter->setBuffer(SFXManager::get()->getBuffer("crash3"), false); - emitter->play(getXYZ()); + buffer = m_crash_sound3; + + getNextEmitter()->play(getXYZ(), buffer); } } // if lin_vel > 0.555 } // if m_bounce_back_time <= 0 @@ -2187,9 +2189,7 @@ void Kart::beep() // If the custom horn can't play (isn't defined) then play the default one if (!playCustomSFX(SFXManager::CUSTOM_HORN)) { - SFXBase* emitter = getNextEmitter(); - emitter->setBuffer(SFXManager::get()->getBuffer("horn"), false); - emitter->play(getXYZ()); + getNextEmitter()->play(getXYZ(), m_horn_sound); } } // beep diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index 3e2f7a307..6d364bd01 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -210,19 +210,19 @@ protected: SFXBase *m_emitter_1; SFXBase *m_emitter_2; SFXBase *m_emitter_3; - //SFXBase *m_beep_sound; SFXBase *m_engine_sound; - //SFXBase *m_crash_sound; - //SFXBase *m_crash_sound2; - //SFXBase *m_crash_sound3; SFXBase *m_terrain_sound; SFXBase *m_nitro_sound; /** A pointer to the previous terrain sound needs to be saved so that an * 'older' sfx can be finished and an abrupt end of the sfx is avoided. */ SFXBase *m_previous_terrain_sound; SFXBase *m_skid_sound; - //SFXBase *m_goo_sound; - //SFXBase *m_boing_sound; + SFXBuffer *m_horn_sound; + SFXBuffer *m_crash_sound; + SFXBuffer *m_crash_sound2; + SFXBuffer *m_crash_sound3; + SFXBuffer *m_goo_sound; + SFXBuffer *m_boing_sound; float m_time_last_crash; RaceManager::KartType m_type; From beb10863c438fef1e89cbd7d56ff6ea4574c24a7 Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Sat, 2 Sep 2017 22:49:36 -0400 Subject: [PATCH 091/125] Continue optimizing OpenAL sources, see #2921 --- src/karts/abstract_kart.hpp | 4 ++- .../controller/local_player_controller.cpp | 29 +++++++++---------- .../controller/local_player_controller.hpp | 11 +++---- src/karts/kart.cpp | 7 +++++ src/karts/kart.hpp | 6 ++-- 5 files changed, 33 insertions(+), 24 deletions(-) diff --git a/src/karts/abstract_kart.hpp b/src/karts/abstract_kart.hpp index 71582aa4b..5bab9703c 100644 --- a/src/karts/abstract_kart.hpp +++ b/src/karts/abstract_kart.hpp @@ -45,6 +45,7 @@ class KartModel; class KartProperties; class Material; class Powerup; +class SFXBuffer; class Skidding; class SlipStream; class TerrainInfo; @@ -472,7 +473,8 @@ public: // ------------------------------------------------------------------------ /** Returns whether this kart is jumping. */ virtual bool isJumping() const = 0; - + // ------------------------------------------------------------------------ + virtual void playSound(SFXBuffer* buffer) = 0; }; // AbstractKart diff --git a/src/karts/controller/local_player_controller.cpp b/src/karts/controller/local_player_controller.cpp index 01eb0879f..2c8b96b47 100644 --- a/src/karts/controller/local_player_controller.cpp +++ b/src/karts/controller/local_player_controller.cpp @@ -65,11 +65,11 @@ LocalPlayerController::LocalPlayerController(AbstractKart *kart, // the right camera once per frame later. Camera *camera = Camera::createCamera(kart); m_camera_index = camera->getIndex(); - m_bzzt_sound = SFXManager::get()->createSoundSource("bzzt"); m_wee_sound = SFXManager::get()->createSoundSource("wee"); - m_ugh_sound = SFXManager::get()->createSoundSource("ugh"); - m_grab_sound = SFXManager::get()->createSoundSource("grab_collectable"); - m_full_sound = SFXManager::get()->createSoundSource("energy_bar_full"); + m_bzzt_sound = SFXManager::get()->getBuffer("bzzt"); + m_ugh_sound = SFXManager::get()->getBuffer("ugh"); + m_grab_sound = SFXManager::get()->getBuffer("grab_collectable"); + m_full_sound = SFXManager::get()->getBuffer("energy_bar_full"); // Attach Particle System Track *track = Track::getCurrentTrack(); @@ -97,11 +97,8 @@ LocalPlayerController::LocalPlayerController(AbstractKart *kart, */ LocalPlayerController::~LocalPlayerController() { - m_bzzt_sound->deleteSFX(); - m_wee_sound ->deleteSFX(); - m_ugh_sound ->deleteSFX(); - m_grab_sound->deleteSFX(); - m_full_sound->deleteSFX(); + m_wee_sound->deleteSFX(); + if (m_sky_particles_emitter) delete m_sky_particles_emitter; } // ~LocalPlayerController @@ -228,7 +225,7 @@ void LocalPlayerController::update(float dt) else if (!m_kart->getKartAnimation() && m_sound_schedule == true) { m_sound_schedule = false; - m_bzzt_sound->play(); + m_kart->playSound(m_bzzt_sound); } } // update @@ -246,7 +243,7 @@ void LocalPlayerController::displayPenaltyWarning() m->addMessage(_("Don't accelerate before go"), m_kart, 2.0f, GUIEngine::getSkin()->getColor("font::normal")); } - m_bzzt_sound->play(); + m_kart->playSound(m_bzzt_sound); } // displayPenaltyWarning //----------------------------------------------------------------------------- @@ -326,31 +323,31 @@ void LocalPlayerController::collectedItem(const Item &item, int add_info, if (old_energy < m_kart->getKartProperties()->getNitroMax() && m_kart->getEnergy() == m_kart->getKartProperties()->getNitroMax()) { - m_full_sound->play(); + m_kart->playSound(m_full_sound); } else if (race_manager->getCoinTarget() > 0 && old_energy < race_manager->getCoinTarget() && m_kart->getEnergy() == race_manager->getCoinTarget()) { - m_full_sound->play(); + m_kart->playSound(m_full_sound); } else { switch(item.getType()) { case Item::ITEM_BANANA: - m_ugh_sound->play(); + m_kart->playSound(m_ugh_sound); break; case Item::ITEM_BUBBLEGUM: //More sounds are played by the kart class //See Kart::collectedItem() - m_ugh_sound->play(); + m_kart->playSound(m_ugh_sound); break; case Item::ITEM_TRIGGER: // no default sound for triggers break; default: - m_grab_sound->play(); + m_kart->playSound(m_grab_sound); break; } } diff --git a/src/karts/controller/local_player_controller.hpp b/src/karts/controller/local_player_controller.hpp index 5493e1b2d..fbe327030 100644 --- a/src/karts/controller/local_player_controller.hpp +++ b/src/karts/controller/local_player_controller.hpp @@ -26,6 +26,7 @@ class AbstractKart; class ParticleEmitter; class SFXBase; +class SFXBuffer; /** PlayerKart manages control events from the player and moves * them to the Kart @@ -47,11 +48,11 @@ private: * camera object is managed in the Camera class, so no need to free it. */ int m_camera_index; - SFXBase *m_bzzt_sound; - SFXBase *m_wee_sound; - SFXBase *m_ugh_sound; - SFXBase *m_grab_sound; - SFXBase *m_full_sound; + SFXBase *m_wee_sound; + SFXBuffer *m_bzzt_sound; + SFXBuffer *m_ugh_sound; + SFXBuffer *m_grab_sound; + SFXBuffer *m_full_sound; virtual void steer(float, int) OVERRIDE; virtual void displayPenaltyWarning() OVERRIDE; diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 5e20d8a67..a1097cd58 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -2994,4 +2994,11 @@ const Vec3& Kart::getNormal() const return m_terrain_info->getNormal(); } // getNormal +// ------------------------------------------------------------------------ + +void Kart::playSound(SFXBuffer* buffer) +{ + getNextEmitter()->play(getXYZ(), buffer); +} + /* EOF */ diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index 6d364bd01..5b7d8902d 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -241,7 +241,6 @@ protected: float getActualWheelForce(); void playCrashSFX(const Material* m, AbstractKart *k); void loadData(RaceManager::KartType type, bool animatedModel); - SFXBase* getNextEmitter(); public: Kart(const std::string& ident, unsigned int world_kart_id, @@ -478,7 +477,10 @@ public: // ------------------------------------------------------------------------ /** Returns whether this kart is jumping. */ virtual bool isJumping() const { return m_is_jumping; }; - + // ------------------------------------------------------------------------ + SFXBase* getNextEmitter(); + // ------------------------------------------------------------------------ + virtual void playSound(SFXBuffer* buffer); }; // Kart From b820a60e9831a296ffb9d8613ae8a591cb9b84ff Mon Sep 17 00:00:00 2001 From: Deve Date: Sun, 3 Sep 2017 23:36:55 +0200 Subject: [PATCH 092/125] Tweak default accelerometer sensitivity. Also move it to the config file. --- src/config/user_config.hpp | 5 +++++ src/input/input_manager.cpp | 7 +++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/config/user_config.hpp b/src/config/user_config.hpp index d59706ab3..bf059c058 100644 --- a/src/config/user_config.hpp +++ b/src/config/user_config.hpp @@ -435,6 +435,11 @@ namespace UserConfigParams &m_multitouch_group, "A parameter in range [0, 0.5] that determines the zone that is " "considered as max value in steering button.")); + + PARAM_PREFIX FloatUserConfigParam m_multitouch_tilt_factor + PARAM_DEFAULT( FloatUserConfigParam(4.0f, "multitouch_tilt_factor", + &m_multitouch_group, + "A parameter that determines general accelerometer sensitivity.")); PARAM_PREFIX FloatUserConfigParam m_multitouch_scale PARAM_DEFAULT( FloatUserConfigParam(1.1f, "multitouch_scale", diff --git a/src/input/input_manager.cpp b/src/input/input_manager.cpp index e604e6b82..9057b888e 100644 --- a/src/input/input_manager.cpp +++ b/src/input/input_manager.cpp @@ -1191,15 +1191,18 @@ EventPropagation InputManager::input(const SEvent& event) if (button->type != BUTTON_STEERING) continue; + + float factor = UserConfigParams::m_multitouch_tilt_factor; + factor = std::max(factor, 0.1f); if (UserConfigParams::m_multitouch_accelerometer == 1) { - button->axis_x = (float)-event.AccelerometerEvent.X / 5.0f; + button->axis_x = (float)-event.AccelerometerEvent.X / factor; device->handleControls(button); } else if (UserConfigParams::m_multitouch_accelerometer == 2) { - button->axis_x = (float)event.AccelerometerEvent.Y / 5.0f; + button->axis_x = (float)event.AccelerometerEvent.Y / factor; device->handleControls(button); } } From bd6c2d9b21072eb97622aa654f9a628ee4e4e0b1 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 4 Sep 2017 08:30:31 +1000 Subject: [PATCH 093/125] Allow setting bevel to 0, fix incorrect Y position when using bevelled values for wheel positions. --- src/karts/kart.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 6379915c9..b14d0d0af 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -606,7 +606,6 @@ void Kart::createPhysics() const Vec3 &bevel = m_kart_properties->getBevelFactor(); Vec3 wheel_pos[4]; - assert(bevel.getX() || bevel.getY() || bevel.getZ()); Vec3 orig_factor(1, 1, 1 - bevel.getZ()); Vec3 bevel_factor(1.0f - bevel.getX(), 1.0f - bevel.getY(), 1.0f); @@ -622,9 +621,9 @@ void Kart::createPhysics() z*kart_length *0.5f); hull->addPoint(p*orig_factor); - hull->addPoint(p*bevel_factor); - // Store the x/z position for the wheels as a weighted average - // of the two bevelled points. + // Only add bevelled point if bevel is defined (i.e.!=0) + if(bevel.length2()>0) + hull->addPoint(p*bevel_factor); if (y == -1) { int index = (x + 1) / 2 + 1 - z; // get index of wheel @@ -636,19 +635,19 @@ void Kart::createPhysics() // All wheel positions are relative to the center of // the collision shape. wheel_pos[index].setX(x*0.5f*kart_width); - // The y position of the wheels (i.e. the points where - // the suspension is attached to) is just at the - // bottom of the kart. That is half the kart height - // down. - wheel_pos[index].setY(- 0.5f*kart_height); wheel_pos[index].setZ((0.5f*kart_length-0.25f)* z); - } else { + // Store the x/z position for the wheels as a weighted average + // of the two bevelled points (y is set below). wheel_pos[index] = p*(orig_factor*(1.0f - f) + bevel_factor*f); - wheel_pos[index].setY(0); } + // The y position of the wheels (i.e. the points where + // the suspension is attached to) is just at the + // bottom of the kart (independent of collision shape). + // That is half the kart height down. + wheel_pos[index].setY(-0.5f*kart_height); } // if y==-1 } // for x } // for z From 5d51b7fcd1336604b135b0ced8c412a6633e5da2 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 4 Sep 2017 08:41:21 +1000 Subject: [PATCH 094/125] Added #define to switch between AI-debugging-topview camera (high up) and physics-debugging-topview camera (close to kart). --- src/graphics/camera_debug.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/graphics/camera_debug.cpp b/src/graphics/camera_debug.cpp index 6069a6be7..29637d5bd 100644 --- a/src/graphics/camera_debug.cpp +++ b/src/graphics/camera_debug.cpp @@ -92,9 +92,7 @@ void CameraDebug::update(float dt) { Camera::update(dt); - // To view inside tunnels in top mode, increase near value - m_camera->setNearValue(m_default_debug_Type==CM_DEBUG_TOP_OF_KART - ? 27.0f : 1.0f); + m_camera->setNearValue(1.0f); float above_kart, cam_angle, side_way, distance; @@ -104,8 +102,17 @@ void CameraDebug::update(float dt) { core::vector3df xyz = m_kart->getXYZ().toIrrVector(); m_camera->setTarget(xyz); +#define CLOSE_TO_KART +#ifdef CLOSE_TO_KART + // Better for debugging physics/collision issues + xyz.Y = xyz.Y+7; + m_camera->setNearValue(7.0f); +#else + // Very high few, better for debugging AI behaviour xyz.Y = xyz.Y+55; xyz.Z -= 5.0f; + m_camera->setNearValue(27.0f); +#endif m_camera->setPosition(xyz); } else if (m_default_debug_Type==CM_DEBUG_SIDE_OF_KART) From b974b35a070fb886b653e535cb1b815daf06c925 Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Sun, 3 Sep 2017 20:37:43 -0400 Subject: [PATCH 095/125] Code cleanup in kart audio code --- src/karts/kart.cpp | 54 +++++++++++++++++----------------------------- src/karts/kart.hpp | 10 ++++----- 2 files changed, 24 insertions(+), 40 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index a1097cd58..1f45aef69 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -171,16 +171,17 @@ Kart::Kart (const std::string& ident, unsigned int world_kart_id, }*/ m_horn_sound = SFXManager::get()->getBuffer("horn"); - m_crash_sound = SFXManager::get()->getBuffer("crash"); - m_crash_sound2 = SFXManager::get()->getBuffer("crash2"); - m_crash_sound3 = SFXManager::get()->getBuffer("crash3"); + m_crash_sounds[0] = SFXManager::get()->getBuffer("crash"); + m_crash_sounds[1] = SFXManager::get()->getBuffer("crash2"); + m_crash_sounds[2] = SFXManager::get()->getBuffer("crash3"); m_goo_sound = SFXManager::get()->getBuffer("goo"); m_boing_sound = SFXManager::get()->getBuffer("boing"); m_engine_sound = SFXManager::get()->createSoundSource(m_kart_properties->getEngineSfxType()); - m_emitter_1 = SFXManager::get()->createSoundSource("crash"); - m_emitter_2 = SFXManager::get()->createSoundSource("crash"); - m_emitter_3 = SFXManager::get()->createSoundSource("crash"); + + for (int i = 0; i < EMITTER_COUNT; i++) + m_emitters[i] = SFXManager::get()->createSoundSource("crash"); + m_skid_sound = SFXManager::get()->createSoundSource( "skid" ); m_nitro_sound = SFXManager::get()->createSoundSource( "nitro" ); m_terrain_sound = NULL; @@ -205,9 +206,9 @@ void Kart::init(RaceManager::KartType type) if (type == RaceManager::KT_PLAYER) factor = std::min(1.0f, race_manager->getNumLocalPlayers()/2.0f); - m_emitter_1->setVolume(factor); - m_emitter_2->setVolume(factor); - m_emitter_3->setVolume(factor); + for (int i = 0; i < EMITTER_COUNT; i++) + m_emitters[i]->setVolume(factor); + m_skid_sound->setVolume(factor); m_nitro_sound->setVolume(factor); } // if getNumLocalPlayers > 1 @@ -259,9 +260,10 @@ Kart::~Kart() m_engine_sound->deleteSFX(); m_skid_sound ->deleteSFX(); - m_emitter_1->deleteSFX(); - m_emitter_2->deleteSFX(); - m_emitter_3->deleteSFX(); + + for (int i = 0; i < EMITTER_COUNT; i++) + m_emitters[i]->deleteSFX(); + m_nitro_sound ->deleteSFX(); delete m_kart_gfx; if(m_terrain_sound) m_terrain_sound->deleteSFX(); @@ -1359,9 +1361,9 @@ void Kart::update(float dt) } */ - m_emitter_1->setPosition(getXYZ()); - m_emitter_2->setPosition(getXYZ()); - m_emitter_3->setPosition(getXYZ()); + for (int i = 0; i < EMITTER_COUNT; i++) + m_emitters[i]->setPosition(getXYZ()); + m_skid_sound->setPosition ( getXYZ() ); m_nitro_sound->setPosition ( getXYZ() ); @@ -2166,15 +2168,8 @@ void Kart::playCrashSFX(const Material* m, AbstractKart *k) } else { - int idx = rand() % 3; - SFXBuffer* buffer; - if (idx == 0) - buffer = m_crash_sound; - else if (idx == 1) - buffer = m_crash_sound2; - else - buffer = m_crash_sound3; - + int idx = rand() % CRASH_SOUND_COUNT; + SFXBuffer* buffer = m_crash_sounds[idx]; getNextEmitter()->play(getXYZ(), buffer); } } // if lin_vel > 0.555 @@ -2688,16 +2683,7 @@ void Kart::kartIsInRestNow() SFXBase* Kart::getNextEmitter() { m_emitter_id = (m_emitter_id + 1) % 3; - - SFXBase* next_emitter; - if (m_emitter_id == 0) - next_emitter = m_emitter_1; - else if (m_emitter_id == 1) - next_emitter = m_emitter_2; - else - next_emitter = m_emitter_3; - - return next_emitter; + return m_emitters[m_emitter_id]; } //----------------------------------------------------------------------------- diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index 5b7d8902d..6353f2022 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -207,9 +207,8 @@ protected: std::vector m_custom_sounds; int m_emitter_id = 0; - SFXBase *m_emitter_1; - SFXBase *m_emitter_2; - SFXBase *m_emitter_3; + static const int EMITTER_COUNT = 3; + SFXBase *m_emitters[EMITTER_COUNT]; SFXBase *m_engine_sound; SFXBase *m_terrain_sound; SFXBase *m_nitro_sound; @@ -218,9 +217,8 @@ protected: SFXBase *m_previous_terrain_sound; SFXBase *m_skid_sound; SFXBuffer *m_horn_sound; - SFXBuffer *m_crash_sound; - SFXBuffer *m_crash_sound2; - SFXBuffer *m_crash_sound3; + static const int CRASH_SOUND_COUNT = 3; + SFXBuffer *m_crash_sounds[CRASH_SOUND_COUNT]; SFXBuffer *m_goo_sound; SFXBuffer *m_boing_sound; float m_time_last_crash; From 416b93b0a152122e8267b71ad9ebf5d4912874da Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 4 Sep 2017 18:08:14 +1000 Subject: [PATCH 096/125] Reset steering when calling reset in a kart, which results in more accurate physics replays. --- src/karts/kart.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index 7c20cbce5..9a5a65df4 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -377,6 +377,8 @@ void Kart::reset() if(m_body) m_body->setDamping(m_kart_properties->getStabilityChassisLinearDamping(), m_kart_properties->getStabilityChassisAngularDamping()); + for (unsigned int i = 0; i < 4; i++) + m_vehicle->getWheelInfo(i).m_steering = 0; if(m_terrain_sound) { From 8a3cdb4fefa77e75e3eef2b6d677111ee09ea2c1 Mon Sep 17 00:00:00 2001 From: Deve Date: Tue, 5 Sep 2017 00:14:21 +0200 Subject: [PATCH 097/125] Don't link with useless libs. Also remove custom script for xrandr. --- CMakeLists.txt | 5 ++--- cmake/FindXrandr.cmake | 18 ------------------ 2 files changed, 2 insertions(+), 21 deletions(-) delete mode 100644 cmake/FindXrandr.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 552f04ac0..56c2d32d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -255,8 +255,7 @@ if(UNIX AND NOT APPLE AND NOT SERVER_ONLY) find_package(X11 REQUIRED) include_directories(${X11_INCLUDE_DIR}) - find_package(Xrandr REQUIRED) - if(NOT XRANDR_FOUND) + if(NOT X11_Xrandr_FOUND) message(FATAL_ERROR "XRANDR not found.") endif() @@ -419,7 +418,7 @@ if(NOT SERVER_ONLY) endif() if(UNIX AND NOT APPLE) - target_link_libraries(supertuxkart ${X11_LIBRARIES} ${XRANDR_LIBRARIES}) + target_link_libraries(supertuxkart ${X11_X11_LIB} ${X11_Xrandr_LIB}) if(USE_LIBBFD) target_link_libraries(supertuxkart ${LIBBFD_LIBRARIES}) endif() diff --git a/cmake/FindXrandr.cmake b/cmake/FindXrandr.cmake deleted file mode 100644 index 820f82be7..000000000 --- a/cmake/FindXrandr.cmake +++ /dev/null @@ -1,18 +0,0 @@ -find_path(XRANDR_INCLUDE_DIR NAMES X11/extensions/Xrandr.h - PATH_SUFFIXES X11/extensions - DOC "The XRANDR include directory" -) - -find_library(XRANDR_LIBRARY NAMES Xrandr - DOC "The XRANDR library" -) - -include(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(XRANDR DEFAULT_MSG XRANDR_LIBRARY XRANDR_INCLUDE_DIR) - -if(XRANDR_FOUND) - set( XRANDR_LIBRARIES ${XRANDR_LIBRARY} ) - set( XRANDR_INCLUDE_DIRS ${XRANDR_INCLUDE_DIR} ) -endif() - -mark_as_advanced(XRANDR_INCLUDE_DIR XRANDR_LIBRARY) From 92aec928a14243cdd1b94650c228cb6ea7c6e798 Mon Sep 17 00:00:00 2001 From: hiker Date: Wed, 6 Sep 2017 09:06:04 +1000 Subject: [PATCH 098/125] Tweaked physics so that wheel raycasts happen from inside the collision shape (fixes karts getting stuck at borders), use a longer less bevelled collision shape better kart stability), and reduce angular factor to make it less likely that karts can drive on walls. --- data/kart_characteristics.xml | 2 +- data/stk_config.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/data/kart_characteristics.xml b/data/kart_characteristics.xml index b4542ba97..c7fd9ad7c 100644 --- a/data/kart_characteristics.xml +++ b/data/kart_characteristics.xml @@ -55,7 +55,7 @@ chassis-angular-damping="0" downward-impulse-factor="5" track-connection-accel="2" - angular-factor="0.5 1.0 0.5" + angular-factor="0.25 1.0 0.5" smooth-flying-impulse="250" /> + restitution="1.0" bevel-factor="0.5 0.0 0.3" + physical-wheel-position="0" /> - diff --git a/data/gfx/explosion_bomb.xml b/data/gfx/explosion_bomb.xml index 18538c256..b953e5599 100644 --- a/data/gfx/explosion_bomb.xml +++ b/data/gfx/explosion_bomb.xml @@ -18,8 +18,8 @@ max="1200" /> - diff --git a/data/gfx/explosion_cake.xml b/data/gfx/explosion_cake.xml index 18538c256..b953e5599 100644 --- a/data/gfx/explosion_cake.xml +++ b/data/gfx/explosion_cake.xml @@ -18,8 +18,8 @@ max="1200" /> - diff --git a/data/gfx/fire.xml b/data/gfx/fire.xml index 4f69ec46c..3c4a0749e 100644 --- a/data/gfx/fire.xml +++ b/data/gfx/fire.xml @@ -18,8 +18,8 @@ max="500" /> - + From 4078863d1d89f2d64a62878c455c68b47150d227 Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Tue, 19 Sep 2017 18:44:05 -0400 Subject: [PATCH 124/125] Update achivements.xml, fixes #2940 --- data/achievements.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data/achievements.xml b/data/achievements.xml index b9f4b1d12..dfa196d5a 100644 --- a/data/achievements.xml +++ b/data/achievements.xml @@ -2,7 +2,7 @@ - + @@ -22,6 +22,7 @@ + From e655c5e97137af61aa435470fd056c168da62409 Mon Sep 17 00:00:00 2001 From: Benau Date: Wed, 20 Sep 2017 13:27:46 +0800 Subject: [PATCH 125/125] Don't initInverseBoneMatrices for < version 3 karts grep -rn speed-weighted-object in deveee's addon karts show nothing (even most addons) --- src/karts/kart_model.cpp | 16 ++++++++++++---- src/karts/kart_model.hpp | 2 ++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/karts/kart_model.cpp b/src/karts/kart_model.cpp index 1d5c659bc..b15355141 100644 --- a/src/karts/kart_model.cpp +++ b/src/karts/kart_model.cpp @@ -96,6 +96,7 @@ void SpeedWeightedObject::Properties::loadFromXMLNode(const XMLNode* xml_node) */ KartModel::KartModel(bool is_master) { + m_version = 0; m_is_master = is_master; m_kart = NULL; m_mesh = NULL; @@ -185,9 +186,8 @@ void KartModel::loadInfo(const XMLNode &node) m_has_nitro_emitter = true; } - unsigned kart_version; - node.get("version", &kart_version); - if (kart_version > 2) + node.get("version", &m_version); + if (m_version > 2) { if (const XMLNode *speed_weighted_objects_node = node.getNode("speed-weighted-objects")) { @@ -335,6 +335,7 @@ KartModel* KartModel::makeCopy(KartRenderType krt) km->m_support_colorization = m_support_colorization; km->m_render_info = new RenderInfo(); km->m_inverse_bone_matrices = m_inverse_bone_matrices; + km->m_version = m_version; km->m_render_info->setKartModelRenderInfo(krt); km->m_nitro_emitter_position[0] = m_nitro_emitter_position[0]; @@ -1207,6 +1208,11 @@ void KartModel::toggleHeadlights(bool on) //----------------------------------------------------------------------------- void KartModel::initInverseBoneMatrices() { + if (m_version < 3) + { + // Only need for >= 3 version of kart + return; + } // Due to irrlicht mesh doesn't expose bone name, we have to create a // dummy aniamted node // All bone matrices are configured in straight frame (as in exporting) @@ -1215,7 +1221,8 @@ void KartModel::initInverseBoneMatrices() float striaght_frame = (float)m_animation_frame[AF_STRAIGHT]; if (m_animation_frame[AF_STRAIGHT] == -1) { - Log::warn("KartModel", "%s has no striaght frame defined."); + Log::warn("KartModel", "%s has no striaght frame defined.", + m_model_filename.c_str()); striaght_frame = 0.0f; } node->setCurrentFrame(striaght_frame); @@ -1245,6 +1252,7 @@ void KartModel::initInverseBoneMatrices() const core::matrix4& KartModel::getInverseBoneMatrix (const std::string& bone_name) const { + assert(m_version >= 3); auto ret = m_inverse_bone_matrices.find(bone_name); assert(ret != m_inverse_bone_matrices.end()); return ret->second; diff --git a/src/karts/kart_model.hpp b/src/karts/kart_model.hpp index 22817c11c..a49a04881 100644 --- a/src/karts/kart_model.hpp +++ b/src/karts/kart_model.hpp @@ -318,6 +318,8 @@ private: std::unordered_map m_inverse_bone_matrices; + unsigned m_version; + // ------------------------------------------------------------------------ void initInverseBoneMatrices(); // ------------------------------------------------------------------------