From a18351c0528a372ee0445a8a2a6dba99c6e18716 Mon Sep 17 00:00:00 2001 From: Deve Date: Thu, 8 Feb 2018 22:34:53 +0100 Subject: [PATCH 1/6] Some fixes for gamepad buttons --- .../source/Irrlicht/CIrrDeviceAndroid.cpp | 68 +++++++++++-------- 1 file changed, 40 insertions(+), 28 deletions(-) diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceAndroid.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceAndroid.cpp index 4f9aeab94..aa1d084ec 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceAndroid.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceAndroid.cpp @@ -575,7 +575,8 @@ s32 CIrrDeviceAndroid::handleKeyboard(AInputEvent* androidEvent) int32_t keyAction = AKeyEvent_getAction(androidEvent); int32_t keyMetaState = AKeyEvent_getMetaState(androidEvent); int32_t keyRepeat = AKeyEvent_getRepeatCount(androidEvent); - + int32_t scanCode = AKeyEvent_getScanCode(androidEvent); + if (keyAction == AKEY_EVENT_ACTION_DOWN) { event.KeyInput.PressedDown = true; @@ -609,22 +610,28 @@ s32 CIrrDeviceAndroid::handleKeyboard(AInputEvent* androidEvent) { getKeyChar(event); } + + // If button doesn't return key code, then at least use device-specific + // scan code, because it's better than nothing + if (event.KeyInput.Key == 0) + { + event.KeyInput.Key = (EKEY_CODE)scanCode; + } // Handle an event when back button in pressed just like an escape key // and also avoid repeating the event to avoid some strange behaviour - if (event.KeyInput.SystemKeyCode == AKEYCODE_BACK) + if (event.KeyInput.SystemKeyCode == AKEYCODE_BACK && + (event.KeyInput.PressedDown == false || keyRepeat > 0)) { - status = 1; - - if (event.KeyInput.PressedDown == false || keyRepeat > 0) - { - ignore_event = true; - } + ignore_event = true; } - // Mark escape key event as handled by application to avoid receiving - // AKEYCODE_BACK key event - if (event.KeyInput.SystemKeyCode == AKEYCODE_ESCAPE) + // Mark escape key and gamepad buttons as handled by application to avoid + // receiving duplicated events + if (event.KeyInput.SystemKeyCode == AKEYCODE_ESCAPE || + event.KeyInput.SystemKeyCode == AKEYCODE_BACK || + (event.KeyInput.SystemKeyCode >= AKEYCODE_BUTTON_A && + event.KeyInput.SystemKeyCode <= AKEYCODE_BUTTON_MODE)) { status = 1; } @@ -740,25 +747,30 @@ s32 CIrrDeviceAndroid::handleGamepad(AInputEvent* androidEvent) int32_t keyCode = AKeyEvent_getKeyCode(androidEvent); int32_t keyAction = AKeyEvent_getAction(androidEvent); int32_t keyRepeat = AKeyEvent_getRepeatCount(androidEvent); + int32_t scanCode = AKeyEvent_getScanCode(androidEvent); - SEvent event; - event.EventType = EET_KEY_INPUT_EVENT; - event.KeyInput.Char = 0; - event.KeyInput.PressedDown = (keyAction == AKEY_EVENT_ACTION_DOWN); - event.KeyInput.Shift = false; - event.KeyInput.Control = false; - event.KeyInput.SystemKeyCode = (u32)keyCode; - event.KeyInput.Key = KeyMap[keyCode]; - - // Handle an event when back button in pressed just like an escape key - // and also avoid repeating the event to avoid some strange behaviour - if (event.KeyInput.SystemKeyCode == AKEYCODE_BACK) + if (keyRepeat == 0) { - status = 1; - ignore = (event.KeyInput.PressedDown == false || keyRepeat > 0); + bool ignore_event = false; + + SEvent event; + event.EventType = EET_KEY_INPUT_EVENT; + event.KeyInput.Char = 0; + event.KeyInput.PressedDown = (keyAction == AKEY_EVENT_ACTION_DOWN); + event.KeyInput.Shift = false; + event.KeyInput.Control = false; + event.KeyInput.SystemKeyCode = (u32)keyCode; + event.KeyInput.Key = KeyMap[keyCode]; + + if (event.KeyInput.Key == 0) + { + event.KeyInput.Key = (EKEY_CODE)scanCode; + } + + postEventFromUser(event); } - postEventFromUser(event); + status = 1; break; } default: @@ -874,7 +886,7 @@ void CIrrDeviceAndroid::createKeyMap() // following look like controller inputs KeyMap[AKEYCODE_BUTTON_A] = IRR_KEY_RETURN; - KeyMap[AKEYCODE_BUTTON_B] = IRR_KEY_BACK; + KeyMap[AKEYCODE_BUTTON_B] = IRR_KEY_ESCAPE; KeyMap[AKEYCODE_BUTTON_C] = IRR_KEY_2; KeyMap[AKEYCODE_BUTTON_X] = IRR_KEY_3; KeyMap[AKEYCODE_BUTTON_Y] = IRR_KEY_4; @@ -886,7 +898,7 @@ void CIrrDeviceAndroid::createKeyMap() KeyMap[AKEYCODE_BUTTON_THUMBL] = IRR_KEY_RETURN; KeyMap[AKEYCODE_BUTTON_THUMBR] = IRR_KEY_RETURN; KeyMap[AKEYCODE_BUTTON_START] = IRR_KEY_RETURN; - KeyMap[AKEYCODE_BUTTON_SELECT] = IRR_KEY_BACK; + KeyMap[AKEYCODE_BUTTON_SELECT] = IRR_KEY_ESCAPE; KeyMap[AKEYCODE_BUTTON_MODE] = IRR_KEY_MENU; KeyMap[AKEYCODE_ESCAPE] = IRR_KEY_ESCAPE; From 434a9c5dccfbb35b7901713fc9c6e5facad4e697 Mon Sep 17 00:00:00 2001 From: hiker Date: Fri, 9 Feb 2018 16:12:44 +1100 Subject: [PATCH 2/6] Fixed compiler warnings. --- src/states_screens/race_gui_overworld.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/states_screens/race_gui_overworld.cpp b/src/states_screens/race_gui_overworld.cpp index 7faa0e5c7..b45dfc909 100644 --- a/src/states_screens/race_gui_overworld.cpp +++ b/src/states_screens/race_gui_overworld.cpp @@ -182,9 +182,10 @@ void RaceGUIOverworld::renderGlobal(float dt) if (race_manager->getIfEmptyScreenSpaceExists() && !GUIEngine::ModalDialog::isADialogActive()) { - const float Sqrt = sqrt(race_manager->getNumLocalPlayers()); - const int rows = ceil(Sqrt); - const int cols = round(Sqrt); + const float sqrt_num_players = + sqrtf((float)race_manager->getNumLocalPlayers()); + const int rows = (int)ceil(sqrt_num_players); + const int cols = (int)round(sqrt_num_players); static video::SColor black = video::SColor(255,0,0,0); GL32_draw2DRectangle(black, irr_driver->getSplitscreenWindow( race_manager->getNumLocalPlayers())); From 4d03fbd1fb18a716889a7bc38e58751edaac0beb Mon Sep 17 00:00:00 2001 From: hiker Date: Sat, 10 Feb 2018 17:55:45 +1100 Subject: [PATCH 3/6] Made the physics time step size configurable in the config file. --- data/stk_config.xml | 2 ++ src/config/stk_config.cpp | 3 +++ src/config/stk_config.hpp | 3 +++ src/physics/physics.cpp | 3 ++- 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/data/stk_config.xml b/data/stk_config.xml index 62a82f12a..6311c5222 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -92,6 +92,7 @@ case (all three normals discarded, the interpolation will just return the normal of the triangle (i.e. de facto no interpolation), but it helps making smoothing much more useful without fixing tracks. + fps: The physics timestep size default-track-friction: Default friction to be used for the track and any track/library pbject. default-moveable-friction: Default friction to be used for any moveable, @@ -99,6 +100,7 @@ --> diff --git a/src/config/stk_config.cpp b/src/config/stk_config.cpp index c022ce3e5..7b4ecc22e 100644 --- a/src/config/stk_config.cpp +++ b/src/config/stk_config.cpp @@ -139,6 +139,7 @@ void STKConfig::load(const std::string &filename) CHECK_NEG(m_replay_dt, "replay delta-t" ); CHECK_NEG(m_smooth_angle_limit, "physics smooth-angle-limit" ); CHECK_NEG(m_default_track_friction, "physics default-track-friction"); + CHECK_NEG(m_physics_fps, "physics fps" ); CHECK_NEG(m_default_moveable_friction, "physics default-moveable-friction"); // Square distance to make distance checks cheaper (no sqrt) @@ -160,6 +161,7 @@ void STKConfig::init_defaults() m_smooth_angle_limit = m_penalty_time = m_default_track_friction = m_default_moveable_friction = UNDEFINED; + m_physics_fps = -100; m_bubblegum_counter = -100; m_shield_restrict_weapos = false; m_max_karts = -100; @@ -249,6 +251,7 @@ void STKConfig::getAllData(const XMLNode * root) physics_node->get("default-track-friction", &m_default_track_friction); physics_node->get("default-moveable-friction", &m_default_moveable_friction); + physics_node->get("fps", &m_physics_fps ); } if (const XMLNode *startup_node= root->getNode("startup")) diff --git a/src/config/stk_config.hpp b/src/config/stk_config.hpp index a04879b35..c5347f223 100644 --- a/src/config/stk_config.hpp +++ b/src/config/stk_config.hpp @@ -93,6 +93,9 @@ public: /** Default friction to be used for any moveable, e.g. karts, balls. */ float m_default_moveable_friction; + /** Default FPS rate for physics. */ + int m_physics_fps; + int m_max_skidmarks; /**stepSimulation(dt, 6, 1.0f/120.0f); + m_dynamics_world->stepSimulation(dt, max_num_steps, + 1.0f / stk_config->m_physics_fps); // Now handle the actual collision. Note: flyables can not be removed // inside of this loop, since the same flyables might hit more than one From c7bc47f2e3c436d0d7fb09715ab60d63db22cf7e Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 12 Feb 2018 09:54:46 +1100 Subject: [PATCH 4/6] Fixed world update to be fixed at the physics frame rate. --- sources.cmake | 2 +- src/main_loop.cpp | 91 ++++++++++++++++++----------------------- src/physics/physics.cpp | 2 +- 3 files changed, 41 insertions(+), 54 deletions(-) diff --git a/sources.cmake b/sources.cmake index d4f28ae4d..ba4868d71 100644 --- a/sources.cmake +++ b/sources.cmake @@ -1,5 +1,5 @@ # Modify this file to change the last-modified date when you add/remove a file. -# This will then trigger a new cmake run automatically. +# This will then trigger a new cmake run automatically. file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp") file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp") file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*") diff --git a/src/main_loop.cpp b/src/main_loop.cpp index 446320c0e..9533d90fe 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -62,6 +62,7 @@ MainLoop::~MainLoop() */ float MainLoop::getLimitedDt() { + m_prev_time = m_curr_time; float dt = 0; // If we are doing a replay, use the dt from the history file if (World::getWorld() && history->replayHistory() ) @@ -79,7 +80,6 @@ float MainLoop::getLimitedDt() } IrrlichtDevice* device = irr_driver->getDevice(); - m_prev_time = m_curr_time; while( 1 ) { @@ -227,82 +227,69 @@ void MainLoop::run() IrrlichtDevice* device = irr_driver->getDevice(); m_curr_time = device->getTimer()->getRealTime(); + // DT keeps track of the leftover time, since the race update + // happens in fixed timesteps + float left_over_time = 0; while(!m_abort) { PROFILER_PUSH_CPU_MARKER("Main loop", 0xFF, 0x00, 0xF7); - m_prev_time = m_curr_time; - float dt = getLimitedDt(); + left_over_time += getLimitedDt(); + int num_steps = int(left_over_time * stk_config->m_physics_fps); + float dt = 1.0f / stk_config->m_physics_fps; + left_over_time -= num_steps * dt ; if (!m_abort && !ProfileWorld::isNoGraphics()) { + float frame_duration = num_steps * dt; + // Render the previous frame, and also handle all user input. PROFILER_PUSH_CPU_MARKER("IrrDriver update", 0x00, 0x00, 0x7F); - irr_driver->update(dt); + irr_driver->update(frame_duration); PROFILER_POP_CPU_MARKER(); - } - if (World::getWorld()) // race is active if world exists - { - PROFILER_PUSH_CPU_MARKER("Update race", 0, 255, 255); - updateRace(dt); - PROFILER_POP_CPU_MARKER(); - } // if race is active - - // We need to check again because update_race may have requested - // the main loop to abort; and it's not a good idea to continue - // since the GUI engine is no more to be called then. - // Also only do music, input, and graphics update if graphics are - // enabled. - if (!m_abort && !ProfileWorld::isNoGraphics()) - { PROFILER_PUSH_CPU_MARKER("Input/GUI", 0x7F, 0x00, 0x00); - input_manager->update(dt); - - #ifdef ENABLE_WIIUSE - wiimote_manager->update(); - #endif - - GUIEngine::update(dt); +#ifdef ENABLE_WIIUSE + wiimote_manager->update(); +#endif + input_manager->update(frame_duration); + GUIEngine::update(frame_duration); PROFILER_POP_CPU_MARKER(); - // 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", 0x7F, 0x00, 0x00); SFXManager::get()->update(); PROFILER_POP_CPU_MARKER(); - PROFILER_PUSH_CPU_MARKER("Protocol manager update", 0x7F, 0x00, 0x7F); - if (STKHost::existHost()) - { - if (STKHost::get()->requestedShutdown()) - STKHost::get()->shutdown(); - else - ProtocolManager::getInstance()->update(dt); - } - PROFILER_POP_CPU_MARKER(); - PROFILER_PUSH_CPU_MARKER("Database polling update", 0x00, 0x7F, 0x7F); - Online::RequestManager::get()->update(dt); + Online::RequestManager::get()->update(frame_duration); PROFILER_POP_CPU_MARKER(); } - else if (!m_abort && ProfileWorld::isNoGraphics()) + + for(int i=0; iisNetworking()) + PROFILER_PUSH_CPU_MARKER("Update race", 0, 255, 255); + if (World::getWorld()) updateRace(dt); + PROFILER_POP_CPU_MARKER(); + + // We need to check again because update_race may have requested + // the main loop to abort; and it's not a good idea to continue + // since the GUI engine is no more to be called then. + if (m_abort) break; + + if (!ProfileWorld::isNoGraphics() && STKHost::existHost() && + STKHost::get()->requestedShutdown()) + { + STKHost::get()->shutdown(); + } + + PROFILER_PUSH_CPU_MARKER("Protocol manager update", + 0x7F, 0x00, 0x7F); + if (NetworkConfig::get()->isNetworking()) ProtocolManager::getInstance()->update(dt); PROFILER_POP_CPU_MARKER(); - PROFILER_PUSH_CPU_MARKER("Database polling update", 0x00, 0x7F, 0x7F); - Online::RequestManager::get()->update(dt); - PROFILER_POP_CPU_MARKER(); - } - - if (World::getWorld() ) - { - World::getWorld()->updateTime(dt); - } + if (World::getWorld()) World::getWorld()->updateTime(dt); + } // while dt > time_step_size PROFILER_POP_CPU_MARKER(); PROFILER_SYNC_FRAME(); diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index ac48be569..11d92b4ba 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -152,7 +152,7 @@ void Physics::update(float dt) // Maximum of three substeps. This will work for framerate down to // 20 FPS (bullet default frequency is 60 HZ). - m_dynamics_world->stepSimulation(dt, max_num_steps, + m_dynamics_world->stepSimulation(dt, stk_config->m_physics_fps / 20, 1.0f / stk_config->m_physics_fps); // Now handle the actual collision. Note: flyables can not be removed From fa20e4f8664118530d4049c40e488bff0b90756c Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 12 Feb 2018 18:01:05 +1100 Subject: [PATCH 5/6] Only issue sfx commands once per rendered frame (during the last substep). --- src/karts/kart.cpp | 10 +++++++--- src/main_loop.cpp | 13 +++++++++---- src/main_loop.hpp | 8 ++++++++ 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index a20cca20e..6cea8de26 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -60,6 +60,7 @@ #include "karts/max_speed.hpp" #include "karts/rescue_animation.hpp" #include "karts/skidding.hpp" +#include "main_loop.hpp" #include "modes/overworld.hpp" #include "modes/soccer_world.hpp" #include "modes/world.hpp" @@ -1755,7 +1756,7 @@ void Kart::handleMaterialSFX(const Material *material) // terrain sound is not necessarily a looping sound so check its status before // setting its speed, to avoid 'ressuscitating' sounds that had already stopped - if(m_terrain_sound && + if(m_terrain_sound && main_loop->isLstSubstep() && (m_terrain_sound->getStatus()==SFXBase::SFX_PLAYING || m_terrain_sound->getStatus()==SFXBase::SFX_PAUSED)) { @@ -2387,10 +2388,13 @@ void Kart::updatePhysics(float dt) */ void Kart::updateEngineSFX(float dt) { - // when going faster, use higher pitch for engine - if(!m_engine_sound || !SFXManager::get()->sfxAllowed()) + // Only update SFX during the last substep (otherwise too many SFX commands + // in one frame), and if sfx are enabled + if(!m_engine_sound || !SFXManager::get()->sfxAllowed() || + !main_loop->isLstSubstep() ) return; + // when going faster, use higher pitch for engine if(isOnGround()) { float max_speed = m_kart_properties->getEngineMaxSpeed(); diff --git a/src/main_loop.cpp b/src/main_loop.cpp index 9533d90fe..71011cdae 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -45,9 +45,10 @@ MainLoop* main_loop = 0; MainLoop::MainLoop() : m_abort(false) { - m_curr_time = 0; - m_prev_time = 0; - m_throttle_fps = true; + m_curr_time = 0; + m_prev_time = 0; + m_throttle_fps = true; + m_is_last_substep = false; } // MainLoop //----------------------------------------------------------------------------- @@ -232,6 +233,7 @@ void MainLoop::run() float left_over_time = 0; while(!m_abort) { + m_is_last_substep = false; PROFILER_PUSH_CPU_MARKER("Main loop", 0xFF, 0x00, 0xF7); left_over_time += getLimitedDt(); @@ -267,6 +269,9 @@ void MainLoop::run() for(int i=0; iupdateTime(dt); } // while dt > time_step_size - + m_is_last_substep = false; PROFILER_POP_CPU_MARKER(); PROFILER_SYNC_FRAME(); } // while !m_abort diff --git a/src/main_loop.hpp b/src/main_loop.hpp index 48d215546..0aae68201 100644 --- a/src/main_loop.hpp +++ b/src/main_loop.hpp @@ -34,6 +34,10 @@ private: /** True if the frame rate should be throttled. */ bool m_throttle_fps; + /** True during the last substep of the inner main loop (where world + * is updated). Used to reduce amount of updates (e.g. sfx positions + * etc). */ + bool m_is_last_substep; Uint32 m_curr_time; Uint32 m_prev_time; float getLimitedDt(); @@ -47,6 +51,10 @@ public: // ------------------------------------------------------------------------ /** Returns true if STK is to be stoppe. */ bool isAborted() const { return m_abort; } + // ------------------------------------------------------------------------ + /** Returns if this is the last substep. Used to reduce the amount + * of updates (e.g. to sfx position) to once per rendered frame. */ + bool isLstSubstep() const { return m_is_last_substep; } }; // MainLoop extern MainLoop* main_loop; From 4ce66e754bfc43be0f878e82b5ec532a09ccbf59 Mon Sep 17 00:00:00 2001 From: hiker Date: Mon, 12 Feb 2018 18:07:51 +1100 Subject: [PATCH 6/6] Fixed compiler warnings. --- src/graphics/irr_driver.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 9758442b0..7626ab568 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -233,19 +233,27 @@ void IrrDriver::updateConfigIfRelevant() core::recti IrrDriver::getSplitscreenWindow(int WindowNum) { const int playernum = race_manager->getNumLocalPlayers(); - const float playernum_sqrt = sqrt(playernum); + const float playernum_sqrt = sqrtf((float)playernum); - int rows = UserConfigParams::split_screen_horizontally ? ceil(playernum_sqrt) : round(playernum_sqrt); - int cols = UserConfigParams::split_screen_horizontally ? round(playernum_sqrt) : ceil(playernum_sqrt); + int rows = int( UserConfigParams::split_screen_horizontally + ? ceil(playernum_sqrt) + : round(playernum_sqrt) ); + int cols = int( UserConfigParams::split_screen_horizontally + ? round(playernum_sqrt) + : ceil(playernum_sqrt) ); if (rows == 0){rows = 1;} if (cols == 0) {cols = 1;} //This could add a bit of overhang - const int width_of_space = ceil((float)irr_driver->getActualScreenSize().Width / (float)cols); - const int height_of_space = ceil((float)irr_driver->getActualScreenSize().Height / (float)rows); + const int width_of_space = + int(ceil( (float)irr_driver->getActualScreenSize().Width + / (float)cols) ); + const int height_of_space = + int (ceil( (float)irr_driver->getActualScreenSize().Height + / (float)rows) ); const int x_grid_Position = WindowNum % cols; - const int y_grid_Position = floor((WindowNum) / cols); + const int y_grid_Position = int(floor((WindowNum) / cols)); int wid = (int)irr_driver->getActualScreenSize().Width; //To prevent the viewport going over the right side, we use std::min to ensure the right corners are never larger than the total width