From 12d70fe3203fce97ac8d17b55f49d6825af8304d Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Wed, 11 Jul 2018 17:45:50 +0200 Subject: [PATCH] virtual slider mouse input mode, minor refactoring, minor bug fixes --- Camera.cpp | 10 +-- EU07.cpp | 10 ++- Track.cpp | 6 +- Train.cpp | 67 ++++++++++-------- Train.h | 2 + command.cpp | 2 +- command.h | 6 +- gamepadinput.cpp | 10 ++- keyboardinput.cpp | 13 ++-- keyboardinput.h | 5 ++ mouseinput.cpp | 169 +++++++++++++++++++++++++++++++++++++++++----- mouseinput.h | 33 +++++++++ scene.cpp | 151 +---------------------------------------- scene.h | 53 +++++++-------- sceneeditor.cpp | 17 ++--- simulation.cpp | 20 +++--- uart.cpp | 4 +- uilayer.cpp | 10 ++- uilayer.h | 4 ++ utilities.h | 7 ++ 20 files changed, 330 insertions(+), 269 deletions(-) diff --git a/Camera.cpp b/Camera.cpp index 6e4f51ed..91e9cbfe 100644 --- a/Camera.cpp +++ b/Camera.cpp @@ -67,8 +67,8 @@ TCamera::OnCommand( command_data const &Command ) { case user_command::viewturn: { OnCursorMove( - reinterpret_cast( Command.param1 ) * 0.005 * Global.fMouseXScale / Global.ZoomFactor, - reinterpret_cast( Command.param2 ) * 0.01 * Global.fMouseYScale / Global.ZoomFactor ); + Command.param1 * 0.005 * Global.fMouseXScale / Global.ZoomFactor, + Command.param2 * 0.01 * Global.fMouseYScale / Global.ZoomFactor ); break; } @@ -86,7 +86,7 @@ TCamera::OnCommand( command_data const &Command ) { 1.0 ); // left-right - auto const movexparam { reinterpret_cast( Command.param1 ) }; + auto const movexparam { Command.param1 }; // 2/3rd of the stick range enables walk speed, past that we lerp between walk and run speed auto const movex { walkspeed + ( std::max( 0.0, std::abs( movexparam ) - 0.65 ) / 0.35 ) * ( movespeed - walkspeed ) }; @@ -96,7 +96,7 @@ TCamera::OnCommand( command_data const &Command ) { 0.0 ); // forward-back - double const movezparam { reinterpret_cast( Command.param2 ) }; + double const movezparam { Command.param2 }; auto const movez { walkspeed + ( std::max( 0.0, std::abs( movezparam ) - 0.65 ) / 0.35 ) * ( movespeed - walkspeed ) }; // NOTE: z-axis is flipped given world coordinate system m_moverate.z = ( @@ -121,7 +121,7 @@ TCamera::OnCommand( command_data const &Command ) { 1.0 ); // up-down - auto const moveyparam { reinterpret_cast( Command.param1 ) }; + auto const moveyparam { Command.param1 }; // 2/3rd of the stick range enables walk speed, past that we lerp between walk and run speed auto const movey { walkspeed + ( std::max( 0.0, std::abs( moveyparam ) - 0.65 ) / 0.35 ) * ( movespeed - walkspeed ) }; diff --git a/EU07.cpp b/EU07.cpp index 0c0b8208..c923cbf9 100644 --- a/EU07.cpp +++ b/EU07.cpp @@ -72,6 +72,7 @@ mouse_input Mouse; gamepad_input Gamepad; glm::dvec2 mouse_pickmodepos; // stores last mouse position in control picking mode std::unique_ptr uart; +user_command command; // currently issued control command, if any } @@ -170,13 +171,13 @@ void key_callback( GLFWwindow *window, int key, int scancode, int action, int mo if( Global.ControlPicking ) { // switch off glfwGetCursorPos( window, &input::mouse_pickmodepos.x, &input::mouse_pickmodepos.y ); - glfwSetInputMode( window, GLFW_CURSOR, GLFW_CURSOR_DISABLED ); + UILayer.set_cursor( GLFW_CURSOR_DISABLED ); glfwSetCursorPos( window, 0, 0 ); } else { // enter picking mode - glfwSetInputMode( window, GLFW_CURSOR, GLFW_CURSOR_NORMAL ); glfwSetCursorPos( window, input::mouse_pickmodepos.x, input::mouse_pickmodepos.y ); + UILayer.set_cursor( GLFW_CURSOR_NORMAL ); } // actually toggle the mode Global.ControlPicking = !Global.ControlPicking; @@ -461,6 +462,11 @@ int main(int argc, char *argv[]) if( true == Global.InputMouse ) { input::Mouse.poll(); } if( true == Global.InputGamepad ) { input::Gamepad.poll(); } if( input::uart != nullptr ) { input::uart->poll(); } + // TODO: wrap current command in object, include other input sources + input::command = ( + input::Mouse.command() != user_command::none ? + input::Mouse.command() : + input::Keyboard.command() ); } } catch( std::bad_alloc const &Error ) { diff --git a/Track.cpp b/Track.cpp index fc2b4347..375b270c 100644 --- a/Track.cpp +++ b/Track.cpp @@ -179,7 +179,7 @@ TTrack * TTrack::Create400m(int what, double dx) trk->Segment->Init( Math3D::vector3( -dx, 0, 0 ), Math3D::vector3( -dx, 0, 400 ), 10.0, 0, 0 ); // prosty trk->location( glm::dvec3{ -dx, 0, 200 } ); //środek, aby się mogło wyświetlić simulation::Paths.insert( trk ); - simulation::Region->insert_path( trk, scene::scratch_data() ); + simulation::Region->insert( trk ); return trk; }; @@ -273,11 +273,11 @@ TTrack * TTrack::NullCreate(int dir) // trzeba jeszcze dodać do odpowiedniego segmentu, aby się renderowały z niego pojazdy trk->location( glm::dvec3{ 0.5 * ( p1 + p2 ) } ); //środek, aby się mogło wyświetlić simulation::Paths.insert( trk ); - simulation::Region->insert_path( trk, scene::scratch_data() ); + simulation::Region->insert( trk ); if( trk2 ) { trk2->location( trk->location() ); // ten sam środek jest simulation::Paths.insert( trk2 ); - simulation::Region->insert_path( trk2, scene::scratch_data() ); + simulation::Region->insert( trk2 ); } return trk; }; diff --git a/Train.cpp b/Train.cpp index 81c0d8a6..2aa14ed0 100644 --- a/Train.cpp +++ b/Train.cpp @@ -26,6 +26,12 @@ http://mozilla.org/MPL/2.0/. #include "mtable.h" #include "Console.h" +namespace input { + +extern user_command command; + +} + void control_mapper::insert( TGauge const &Gauge, std::string const &Label ) { @@ -922,7 +928,7 @@ void TTrain::OnCommand_independentbrakeset( TTrain *Train, command_data const &C Train->mvControlled->LocalBrakePosA = ( clamp( - reinterpret_cast( Command.param1 ), + Command.param1, 0.0, 1.0 ) ); /* Train->mvControlled->LocalBrakePos = ( @@ -931,7 +937,7 @@ void TTrain::OnCommand_independentbrakeset( TTrain *Train, command_data const &C 0.0, LocalBrakePosNo, clamp( - reinterpret_cast( Command.param1 ), + Command.param1, 0.0, 1.0 ) ) ) ); */ } @@ -1003,26 +1009,16 @@ void TTrain::OnCommand_trainbrakedecrease( TTrain *Train, command_data const &Co Train->set_train_brake( Train->mvOccupied->BrakeCtrlPos - Global.fBrakeStep ); } } - else { - // release - if( ( Train->mvOccupied->BrakeCtrlPos == -1 ) - && ( Train->mvOccupied->BrakeHandle == FVel6 ) - && ( Train->DynamicObject->Controller != AIdriver ) - && ( Global.iFeedbackMode < 3 ) ) { - // Odskakiwanie hamulce EP - Train->set_train_brake( 0 ); - } - } } void TTrain::OnCommand_trainbrakeset( TTrain *Train, command_data const &Command ) { - Train->mvControlled->BrakeLevelSet( + Train->mvOccupied->BrakeLevelSet( interpolate( - Train->mvControlled->Handle->GetPos( bh_MIN ), - Train->mvControlled->Handle->GetPos( bh_MAX ), + Train->mvOccupied->Handle->GetPos( bh_MIN ), + Train->mvOccupied->Handle->GetPos( bh_MAX ), clamp( - reinterpret_cast( Command.param1 ), + Command.param1, 0.0, 1.0 ) ) ); } @@ -4334,18 +4330,35 @@ bool TTrain::Update( double const Deltatime ) { // Ra: TODO: odczyty klawiatury/pulpitu nie powinny być uzależnione od istnienia modelu kabiny if( ( DynamicObject->Mechanik != nullptr ) - && ( false == DynamicObject->Mechanik->AIControllFlag ) // nie blokujemy AI - && ( ( mvOccupied->TrainType == dt_ET40 ) - || ( mvOccupied->TrainType == dt_EP05 ) ) ) { - // dla ET40 i EU05 automatyczne cofanie nastawnika - i tak nie będzie to działać dobrze... - // TODO: remove direct keyboard check, use deltatime to stabilize speed - if( ( glfwGetKey( Global.window, GLFW_KEY_KP_ADD ) != GLFW_TRUE ) - && ( mvOccupied->MainCtrlPos > mvOccupied->MainCtrlActualPos ) ) { - mvOccupied->DecMainCtrl( 1 ); + && ( false == DynamicObject->Mechanik->AIControllFlag ) ) { + // nie blokujemy AI + if( ( mvOccupied->TrainType == dt_ET40 ) + || ( mvOccupied->TrainType == dt_EP05 ) ) { + // dla ET40 i EU05 automatyczne cofanie nastawnika - i tak nie będzie to działać dobrze... + // TODO: use deltatime to stabilize speed + if( false == ( + ( input::command == user_command::mastercontrollerset ) + || ( input::command == user_command::mastercontrollerincrease ) + || ( input::command == user_command::mastercontrollerdecrease ) ) ) { + if( mvOccupied->MainCtrlPos > mvOccupied->MainCtrlActualPos ) { + mvOccupied->DecMainCtrl( 1 ); + } + else if( mvOccupied->MainCtrlPos < mvOccupied->MainCtrlActualPos ) { + // Ra 15-01: a to nie miało być tylko cofanie? + mvOccupied->IncMainCtrl( 1 ); + } + } } - if( ( glfwGetKey( Global.window, GLFW_KEY_KP_SUBTRACT ) != GLFW_TRUE ) - && ( mvOccupied->MainCtrlPos < mvOccupied->MainCtrlActualPos ) ) { - mvOccupied->IncMainCtrl( 1 ); // Ra 15-01: a to nie miało być tylko cofanie? + + if( ( mvOccupied->BrakeHandle == FVel6 ) + && ( mvOccupied->fBrakeCtrlPos < 0.0 ) + && ( Global.iFeedbackMode < 3 ) ) { + // Odskakiwanie hamulce EP + if( false == ( + ( input::command == user_command::trainbrakeset ) + || ( input::command == user_command::trainbrakedecrease ) ) ) { + set_train_brake( 0 ); + } } } diff --git a/Train.h b/Train.h index 10416620..af61e8d4 100644 --- a/Train.h +++ b/Train.h @@ -648,6 +648,8 @@ private: inline TDynamicObject const *Dynamic() const { return DynamicObject; }; inline TMoverParameters *Controlled() { return mvControlled; }; inline TMoverParameters const *Controlled() const { return mvControlled; }; + inline TMoverParameters *Occupied() { return mvOccupied; }; + inline TMoverParameters const *Occupied() const { return mvOccupied; }; void DynamicSet(TDynamicObject *d); }; diff --git a/command.cpp b/command.cpp index 85d33b61..f3acb430 100644 --- a/command.cpp +++ b/command.cpp @@ -243,7 +243,7 @@ command_queue::pop( command_data &Command, std::size_t const Recipient ) { } void -command_relay::post( user_command const Command, std::uint64_t const Param1, std::uint64_t const Param2, int const Action, std::uint16_t const Recipient ) const { +command_relay::post( user_command const Command, double const Param1, double const Param2, int const Action, std::uint16_t const Recipient ) const { auto const &command = simulation::Commands_descriptions[ static_cast( Command ) ]; if( ( command.target == command_target::vehicle ) diff --git a/command.h b/command.h index d44e90fd..2b9dd446 100644 --- a/command.h +++ b/command.h @@ -231,8 +231,8 @@ struct command_data { user_command command; int action; // press, repeat or release - std::uint64_t param1; - std::uint64_t param2; + double param1; + double param2; double time_delta; }; @@ -282,7 +282,7 @@ public: // posts specified command for the specified recipient // TODO: replace uint16_t with recipient handle, based on item id void - post( user_command const Command, std::uint64_t const Param1, std::uint64_t const Param2, + post( user_command const Command, double const Param1, double const Param2, int const Action, std::uint16_t const Recipient ) const; private: // types diff --git a/gamepadinput.cpp b/gamepadinput.cpp index ec433f1c..95039614 100644 --- a/gamepadinput.cpp +++ b/gamepadinput.cpp @@ -198,8 +198,8 @@ gamepad_input::process_axes( glm::vec2 Leftstick, glm::vec2 const &Rightstick, g double const turny = -Rightstick.y * 10.0 * deltatime; m_relay.post( user_command::viewturn, - reinterpret_cast( turnx ), - reinterpret_cast( turny ), + turnx, + turny, GLFW_PRESS, // as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0 // TODO: pass correct entity id once the missing systems are in place @@ -211,12 +211,10 @@ gamepad_input::process_axes( glm::vec2 Leftstick, glm::vec2 const &Rightstick, g if( ( Leftstick.x != 0.0 || Leftstick.y != 0.0 ) || ( m_leftstick.x != 0.0 || m_leftstick.y != 0.0 ) ) { - double const movex = static_cast( Leftstick.x ); - double const movez = static_cast( Leftstick.y ); m_relay.post( user_command::movehorizontal, - reinterpret_cast( movex ), - reinterpret_cast( movez ), + Leftstick.x, + Leftstick.y, GLFW_PRESS, 0 ); } diff --git a/keyboardinput.cpp b/keyboardinput.cpp index acede6ab..63b9897f 100644 --- a/keyboardinput.cpp +++ b/keyboardinput.cpp @@ -161,6 +161,10 @@ keyboard_input::key( int const Key, int const Action ) { // as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0 // TODO: pass correct entity id once the missing systems are in place m_relay.post( lookup->second, 0, 0, Action, 0 ); + m_command = ( + Action == GLFW_RELEASE ? + user_command::none : + lookup->second ); return true; } @@ -600,14 +604,12 @@ keyboard_input::poll() { if( ( movementhorizontal.x != 0.f || movementhorizontal.y != 0.f ) || ( m_movementhorizontal.x != 0.f || m_movementhorizontal.y != 0.f ) ) { - double const movexparam = static_cast( movementhorizontal.x ); - double const movezparam = static_cast( movementhorizontal.y ); m_relay.post( ( true == Global.ctrlState ? user_command::movehorizontalfast : user_command::movehorizontal ), - reinterpret_cast( movexparam ), - reinterpret_cast( movezparam ), + movementhorizontal.x, + movementhorizontal.y, GLFW_PRESS, 0 ); } @@ -623,12 +625,11 @@ keyboard_input::poll() { if( ( movementvertical != 0.f ) || ( m_movementvertical != 0.f ) ) { - double const moveyparam = static_cast( movementvertical ); m_relay.post( ( true == Global.ctrlState ? user_command::moveverticalfast : user_command::movevertical ), - reinterpret_cast( moveyparam ), + movementvertical, 0, GLFW_PRESS, 0 ); diff --git a/keyboardinput.h b/keyboardinput.h index ab5bb0bc..720dde3a 100644 --- a/keyboardinput.h +++ b/keyboardinput.h @@ -28,6 +28,10 @@ public: key( int const Key, int const Action ); void poll(); + inline + user_command const + command() const { + return m_command; } private: // types @@ -65,6 +69,7 @@ private: // members commandsetup_sequence m_commands; + user_command m_command; // last, if any, issued command usercommand_map m_bindings; command_relay m_relay; bool m_shift{ false }; diff --git a/mouseinput.cpp b/mouseinput.cpp index 4bfceae0..af45af26 100644 --- a/mouseinput.cpp +++ b/mouseinput.cpp @@ -16,9 +16,111 @@ http://mozilla.org/MPL/2.0/. #include "world.h" #include "train.h" #include "renderer.h" +#include "uilayer.h" extern TWorld World; +void +mouse_slider::bind( user_command const &Command ) { + + m_command = Command; + + auto const *train { World.train() }; + TMoverParameters const *vehicle { nullptr }; + switch( m_command ) { + case user_command::mastercontrollerset: + case user_command::secondcontrollerset: { + vehicle = ( train ? train->Controlled() : nullptr ); + break; + } + case user_command::trainbrakeset: + case user_command::independentbrakeset: { + vehicle = ( train ? train->Occupied() : nullptr ); + break; + } + default: { + break; + } + } + if( vehicle == nullptr ) { return; } + + // calculate initial value and accepted range + switch( m_command ) { + case user_command::mastercontrollerset: { + m_valuerange = ( + vehicle->CoupledCtrl ? + vehicle->MainCtrlPosNo + vehicle->ScndCtrlPosNo : + vehicle->MainCtrlPosNo ); + m_value = ( + vehicle->CoupledCtrl ? + vehicle->MainCtrlPos + vehicle->ScndCtrlPos : + vehicle->MainCtrlPos ); + m_analogue = false; + break; + } + case user_command::secondcontrollerset: { + m_valuerange = vehicle->ScndCtrlPosNo; + m_value = vehicle->ScndCtrlPos; + m_analogue = false; + break; + } + case user_command::trainbrakeset: { + m_valuerange = 1.0; + m_value = ( vehicle->fBrakeCtrlPos - vehicle->Handle->GetPos( bh_MIN ) ) / ( vehicle->Handle->GetPos( bh_MAX ) - vehicle->Handle->GetPos( bh_MIN ) ); + m_analogue = true; + break; + } + case user_command::independentbrakeset: { + m_valuerange = 1.0; + m_value = vehicle->LocalBrakePosA; + m_analogue = true; + break; + } + default: { + m_valuerange = 1; + break; + } + } + // hide the cursor and place it in accordance with current slider value + glfwGetCursorPos( Global.window, &m_cursorposition.x, &m_cursorposition.y ); + UILayer.set_cursor( GLFW_CURSOR_DISABLED ); + + auto const controlsize { Global.iWindowHeight * 0.75 }; + auto const controledge { Global.iWindowHeight * 0.5 + controlsize * 0.5 }; + auto const stepsize { controlsize / m_valuerange }; + + glfwSetCursorPos( + Global.window, + Global.iWindowWidth * 0.5, + ( m_analogue ? + controledge - ( 1.0 - m_value ) * controlsize : + controledge - m_value * stepsize - 0.5 * stepsize ) ); +} + +void +mouse_slider::release() { + + m_command = user_command::none; + glfwSetCursorPos( Global.window, m_cursorposition.x, m_cursorposition.y ); + UILayer.set_cursor( GLFW_CURSOR_NORMAL ); +} + +void +mouse_slider::on_move( double const Mousex, double const Mousey ) { + + auto const controlsize { Global.iWindowHeight * 0.75 }; + auto const controledge { Global.iWindowHeight * 0.5 + controlsize * 0.5 }; + auto const stepsize { controlsize / m_valuerange }; + + auto mousey = clamp( Mousey, controledge - controlsize, controledge ); + m_value = ( + m_analogue ? + 1.0 - ( ( controledge - mousey ) / controlsize ) : + std::floor( ( controledge - mousey ) / stepsize ) ); +} + + + bool mouse_input::init() { @@ -40,8 +142,8 @@ mouse_input::move( double Mousex, double Mousey ) { // default control mode m_relay.post( user_command::viewturn, - reinterpret_cast( Mousex ), - reinterpret_cast( Mousey ), + Mousex, + Mousey, GLFW_PRESS, // as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0 // TODO: pass correct entity id once the missing systems are in place @@ -49,6 +151,17 @@ mouse_input::move( double Mousex, double Mousey ) { } else { // control picking mode + if( m_slider.command() != user_command::none ) { + m_slider.on_move( Mousex, Mousey ); + m_relay.post( + m_slider.command(), + m_slider.value(), + 0, + GLFW_PRESS, + // TODO: pass correct entity id once the missing systems are in place + 0 ); + } + if( false == m_pickmodepanning ) { // even if the view panning isn't active we capture the cursor position in case it does get activated m_cursorposition.x = Mousex; @@ -59,8 +172,8 @@ mouse_input::move( double Mousex, double Mousey ) { auto const viewoffset = cursorposition - m_cursorposition; m_relay.post( user_command::viewturn, - reinterpret_cast( viewoffset.x ), - reinterpret_cast( viewoffset.y ), + viewoffset.x, + viewoffset.y, GLFW_PRESS, // as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0 // TODO: pass correct entity id once the missing systems are in place @@ -109,6 +222,11 @@ mouse_input::button( int const Button, int const Action ) { mousecommand = user_command::none; } else { + if( Button == GLFW_MOUSE_BUTTON_LEFT ) { + if( m_slider.command() != user_command::none ) { + m_slider.release(); + } + } // if it's the right mouse button that got released and we had no command active, we were potentially in view panning mode; stop it if( Button == GLFW_MOUSE_BUTTON_RIGHT ) { m_pickmodepanning = false; @@ -140,11 +258,6 @@ mouse_input::button( int const Button, int const Action ) { default: { break; } } } - // NOTE: basic keyboard controls don't have any parameters - // NOTE: as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0 - // TODO: pass correct entity id once the missing systems are in place - m_relay.post( mousecommand, 0, 0, Action, 0 ); - m_updateaccumulator = -0.25; // prevent potential command repeat right after issuing one switch( mousecommand ) { case user_command::mastercontrollerincrease: @@ -161,10 +274,23 @@ mouse_input::button( int const Button, int const Action ) { m_varyingpollrate = true; break; } + case user_command::mastercontrollerset: + case user_command::secondcontrollerset: + case user_command::trainbrakeset: + case user_command::independentbrakeset: { + m_slider.bind( mousecommand ); + mousecommand = user_command::none; + return; + } default: { break; } } + // NOTE: basic keyboard controls don't have any parameters + // NOTE: as we haven't yet implemented either item id system or multiplayer, the 'local' controlled vehicle and entity have temporary ids of 0 + // TODO: pass correct entity id once the missing systems are in place + m_relay.post( mousecommand, 0, 0, Action, 0 ); + m_updateaccumulator = -0.25; // prevent potential command repeat right after issuing one } else { // if we don't have any recognized element under the cursor and the right button was pressed, enter view panning mode @@ -204,16 +330,25 @@ mouse_input::poll() { } } +user_command +mouse_input::command() const { + + return ( + m_slider.command() != user_command::none ? m_slider.command() : + m_mousecommandleft != user_command::none ? m_mousecommandleft : + m_mousecommandright ); +} + void mouse_input::default_bindings() { m_mousecommands = { { "mainctrl:", { - user_command::mastercontrollerincrease, - user_command::mastercontrollerdecrease } }, + user_command::mastercontrollerset, + user_command::none } }, { "scndctrl:", { - user_command::secondcontrollerincrease, - user_command::secondcontrollerdecrease } }, + user_command::secondcontrollerset, + user_command::none } }, { "shuntmodepower:", { user_command::secondcontrollerincrease, user_command::secondcontrollerdecrease } }, @@ -221,11 +356,11 @@ mouse_input::default_bindings() { user_command::reverserincrease, user_command::reverserdecrease } }, { "brakectrl:", { - user_command::trainbrakeincrease, - user_command::trainbrakedecrease } }, + user_command::trainbrakeset, + user_command::none } }, { "localbrake:", { - user_command::independentbrakeincrease, - user_command::independentbrakedecrease } }, + user_command::independentbrakeset, + user_command::none } }, { "manualbrake:", { user_command::manualbrakeincrease, user_command::manualbrakedecrease } }, diff --git a/mouseinput.h b/mouseinput.h index e87cdc42..c0fa2cb2 100644 --- a/mouseinput.h +++ b/mouseinput.h @@ -12,6 +12,36 @@ http://mozilla.org/MPL/2.0/. #include #include "command.h" +// virtual slider; value determined by position of the mouse +class mouse_slider { + +public: +// constructors + mouse_slider() = default; +// methods + void + bind( user_command const &Command ); + void + release(); + void + on_move( double const Mousex, double const Mousey ); + inline + user_command + command() const { + return m_command; } + double + value() const { + return m_value; } + +private: +// members + user_command m_command { user_command::none }; + double m_value { 0.0 }; + double m_valuerange { 0.0 }; + bool m_analogue { false }; + glm::dvec2 m_cursorposition { 0.0 }; +}; + class mouse_input { public: @@ -27,6 +57,8 @@ public: move( double const Mousex, double const Mousey ); void poll(); + user_command + command() const; private: // types @@ -44,6 +76,7 @@ private: // members command_relay m_relay; + mouse_slider m_slider; // virtual control, when active translates intercepted mouse position to a value controlcommands_map m_mousecommands; user_command m_mousecommandleft { user_command::none }; // last if any command issued with left mouse button user_command m_mousecommandright { user_command::none }; // last if any command issued with right mouse button diff --git a/scene.cpp b/scene.cpp index 569af7e3..003e2622 100644 --- a/scene.cpp +++ b/scene.cpp @@ -1118,7 +1118,7 @@ basic_region::RadioStop( glm::dvec3 const &Location ) { } void -basic_region::insert_shape( shape_node Shape, scratch_data &Scratchpad, bool const Transform ) { +basic_region::insert( shape_node Shape, scratch_data &Scratchpad, bool const Transform ) { // shape might need to be split into smaller pieces, so we create list of nodes instead of just single one // using deque so we can do single pass iterating and addding generated pieces without invalidating anything @@ -1182,7 +1182,7 @@ basic_region::insert_shape( shape_node Shape, scratch_data &Scratchpad, bool con // inserts provided lines in the region void -basic_region::insert_lines( lines_node Lines, scratch_data &Scratchpad ) { +basic_region::insert( lines_node Lines, scratch_data &Scratchpad ) { if( Lines.m_data.vertices.empty() ) { return; } // transform point coordinates if needed @@ -1224,135 +1224,6 @@ basic_region::insert_lines( lines_node Lines, scratch_data &Scratchpad ) { } } -// inserts provided track in the region -void -basic_region::insert_path( TTrack *Path, scratch_data &Scratchpad ) { - // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done - auto location = Path->location(); - - if( point_inside( location ) ) { - // NOTE: nodes placed outside of region boundaries are discarded - section( location ).insert( Path ); - } - else { - // tracks are guaranteed to hava a name so we can skip the check - ErrorLog( "Bad scenario: track node \"" + Path->name() + "\" placed in location outside region bounds (" + to_string( location ) + ")" ); - } - // also register path ends in appropriate sections, for path merging lookups - // TODO: clean this up during track refactoring - for( auto &point : Path->endpoints() ) { - register_path( Path, point ); - } -} - -// inserts provided track in the region -void -basic_region::insert_traction( TTraction *Traction, scratch_data &Scratchpad ) { - // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done - auto location = Traction->location(); - - if( point_inside( location ) ) { - // NOTE: nodes placed outside of region boundaries are discarded - section( location ).insert( Traction ); - } - else { - // tracks are guaranteed to hava a name so we can skip the check - ErrorLog( "Bad scenario: traction node \"" + Traction->name() + "\" placed in location outside region bounds (" + to_string( location ) + ")" ); - } - // also register traction ends in appropriate sections, for path merging lookups - // TODO: clean this up during track refactoring - for( auto &point : Traction->endpoints() ) { - register_traction( Traction, point ); - } -} - -// inserts provided instance of 3d model in the region -void -basic_region::insert_instance( TAnimModel *Instance, scratch_data &Scratchpad ) { - - auto const location { Instance->location() }; - - if( point_inside( location ) ) { - // NOTE: nodes placed outside of region boundaries are discarded - section( location ).insert( Instance ); - } - else { - // tracks are guaranteed to hava a name so we can skip the check - ErrorLog( "Bad scenario: model node \"" + Instance->name() + "\" placed in location outside region bounds (" + to_string( location ) + ")" ); - } -} - -// removes specified instance of 3d model from the region -void -basic_region::erase_instance( TAnimModel *Instance ) { - - auto const location { Instance->location() }; - - if( point_inside( location ) ) { - // NOTE: nodes placed outside of region boundaries are discarded - section( location ).erase( Instance ); - } -} - -// removes specified memory cell from the region -void -basic_region::erase_memorycell( TMemCell *Memorycell ) { - - auto const location { Memorycell->location() }; - - if( point_inside( location ) ) { - section( location ).erase( Memorycell ); - } -} - -// inserts provided sound in the region -void -basic_region::insert_sound( sound_source *Sound, scratch_data &Scratchpad ) { - // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done - auto location = Sound->location(); - - if( point_inside( location ) ) { - // NOTE: nodes placed outside of region boundaries are discarded - section( location ).insert( Sound ); - } - else { - // tracks are guaranteed to hava a name so we can skip the check - ErrorLog( "Bad scenario: sound node \"" + Sound->name() + "\" placed in location outside region bounds (" + to_string( location ) + ")" ); - } -} - -// inserts provided event launcher in the region -void -basic_region::insert_launcher( TEventLauncher *Launcher, scratch_data &Scratchpad ) { - // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done - auto location = Launcher->location(); - - if( point_inside( location ) ) { - // NOTE: nodes placed outside of region boundaries are discarded - section( location ).insert( Launcher ); - } - else { - // tracks are guaranteed to hava a name so we can skip the check - ErrorLog( "Bad scenario: event launcher \"" + Launcher->name() + "\" placed in location outside region bounds (" + to_string( location ) + ")" ); - } -} - -// inserts provided memory cell in the region -void -basic_region::insert_memorycell( TMemCell *Memorycell, scratch_data &Scratchpad ) { - // NOTE: bounding area isn't present/filled until track class and wrapper refactoring is done - auto location = Memorycell->location(); - - if( point_inside( location ) ) { - // NOTE: nodes placed outside of region boundaries are discarded - section( location ).insert( Memorycell ); - } - else { - // tracks are guaranteed to hava a name so we can skip the check - ErrorLog( "Bad scenario: memory cell \"" + Memorycell->name() + "\" placed in location outside region bounds (" + to_string( location ) + ")" ); - } -} - // find a vehicle located neares to specified location, within specified radius, optionally discarding vehicles without drivers std::tuple basic_region::find_vehicle( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled, bool const Findbycoupler ) { @@ -1463,24 +1334,6 @@ basic_region::sections( glm::dvec3 const &Point, float const Radius ) { return m_scratchpad.sections; } -// registers specified path in the lookup directory of a cell enclosing specified point -void -basic_region::register_path( TTrack *Path, glm::dvec3 const &Point ) { - - if( point_inside( Point ) ) { - section( Point ).register_node( Path, Point ); - } -} - -// registers specified end point of the provided traction piece in the lookup directory of the region -void -basic_region::register_traction( TTraction *Traction, glm::dvec3 const &Point ) { - - if( point_inside( Point ) ) { - section( Point ).register_node( Traction, Point ); - } -} - // checks whether specified point is within boundaries of the region bool basic_region::point_inside( glm::dvec3 const &Location ) { diff --git a/scene.h b/scene.h index 041c8443..78ebfbe6 100644 --- a/scene.h +++ b/scene.h @@ -339,34 +339,35 @@ public: RadioStop( glm::dvec3 const &Location ); // inserts provided shape in the region void - insert_shape( shape_node Shape, scratch_data &Scratchpad, bool const Transform ); + insert( shape_node Shape, scratch_data &Scratchpad, bool const Transform ); // inserts provided lines in the region void - insert_lines( lines_node Lines, scratch_data &Scratchpad ); - // inserts provided track in the region + insert( lines_node Lines, scratch_data &Scratchpad ); + // inserts provided node in the region + template void - insert_path( TTrack *Path, scratch_data &Scratchpad ); - // inserts provided track in the region + insert( Type_ *Node ) { + auto const location { Node->location() }; + if( false == point_inside( location ) ) { + // NOTE: nodes placed outside of region boundaries are discarded + // TBD, TODO: clamp coordinates to region boundaries? + return; } + section( location ).insert( Node ); } + // inserts provided node in the region and registers its ends in lookup directory + template void - insert_traction( TTraction *Traction, scratch_data &Scratchpad ); - // inserts provided instance of 3d model in the region + insert_and_register( Type_ *Node ) { + insert( Node ); + for( auto const &point : Node->endpoints() ) { + if( point_inside( point ) ) { + section( point ).register_node( Node, point ); } } } + // removes specified node from the region + template void - insert_instance( TAnimModel *Instance, scratch_data &Scratchpad ); - // inserts provided sound in the region - void - insert_sound( sound_source *Sound, scratch_data &Scratchpad ); - // inserts provided event launcher in the region - void - insert_launcher( TEventLauncher *Launcher, scratch_data &Scratchpad ); - // inserts provided memory cell in the region - void - insert_memorycell( TMemCell *Memorycell, scratch_data &Scratchpad ); - // removes specified instance of 3d model from the region - void - erase_instance( TAnimModel *Instance ); - // removes specified memory cell from the region - void - erase_memorycell( TMemCell *Memorycell ); + erase( Type_ *Node ) { + auto const location{ Node->location() }; + if( point_inside( location ) ) { + section( location ).erase( Node ); } } // find a vehicle located nearest to specified point, within specified radius. reurns: located vehicle and distance std::tuple find_vehicle( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled, bool const Findbycoupler ); @@ -393,12 +394,6 @@ private: }; // methods - // registers specified end point of the provided path in the lookup directory of the region - void - register_path( TTrack *Path, glm::dvec3 const &Point ); - // registers specified end point of the provided traction piece in the lookup directory of the region - void - register_traction( TTraction *Traction, glm::dvec3 const &Point ); // checks whether specified point is within boundaries of the region bool point_inside( glm::dvec3 const &Location ); diff --git a/sceneeditor.cpp b/sceneeditor.cpp index 51289923..c8144244 100644 --- a/sceneeditor.cpp +++ b/sceneeditor.cpp @@ -12,6 +12,7 @@ http://mozilla.org/MPL/2.0/. #include "globals.h" #include "simulation.h" +#include "uilayer.h" #include "renderer.h" namespace scene { @@ -46,7 +47,7 @@ basic_editor::on_mouse_button( int const Button, int const Action ) { m_node = GfxRenderer.Update_Pick_Node(); m_nodesnapshot = { m_node }; if( m_node ) { - glfwSetInputMode( Global.window, GLFW_CURSOR, GLFW_CURSOR_DISABLED ); + UILayer.set_cursor( GLFW_CURSOR_DISABLED ); } } else { @@ -54,7 +55,7 @@ basic_editor::on_mouse_button( int const Button, int const Action ) { // TODO: record the current undo step on the undo stack m_nodesnapshot = { m_node }; if( m_node ) { - glfwSetInputMode( Global.window, GLFW_CURSOR, GLFW_CURSOR_NORMAL ); + UILayer.set_cursor( GLFW_CURSOR_NORMAL ); } } @@ -136,9 +137,9 @@ basic_editor::translate( float const Offset ) { void basic_editor::translate_instance( TAnimModel *Instance, glm::dvec3 const &Location ) { - simulation::Region->erase_instance( Instance ); + simulation::Region->erase( Instance ); Instance->location( Location ); - simulation::Region->insert_instance( Instance, scene::scratch_data() ); + simulation::Region->insert( Instance ); } void @@ -152,9 +153,9 @@ basic_editor::translate_instance( TAnimModel *Instance, float const Offset ) { void basic_editor::translate_memorycell( TMemCell *Memorycell, glm::dvec3 const &Location ) { - simulation::Region->erase_memorycell( Memorycell ); + simulation::Region->erase( Memorycell ); Memorycell->location( Location ); - simulation::Region->insert_memorycell( Memorycell, scene::scratch_data() ); + simulation::Region->insert( Memorycell ); } void @@ -182,8 +183,8 @@ basic_editor::rotate_instance( TAnimModel *Instance, glm::vec3 const &Angle ) { glm::vec3 angle = glm::dvec3 { Instance->Angles() }; angle.y = clamp_circular( angle.y + Angle.y, 360.f ); if( mode_snap() ) { - auto const quantizationstep { 15.f }; - angle.y = quantizationstep * std::round( angle.y * ( 1.f / quantizationstep ) ); + // TBD, TODO: adjustable quantization step + angle.y = quantize( angle.y, 15.f ); } Instance->Angles( angle ); // update scene diff --git a/simulation.cpp b/simulation.cpp index 39ab215b..e5a7dd65 100644 --- a/simulation.cpp +++ b/simulation.cpp @@ -327,7 +327,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad delete pathnode; */ } - simulation::Region->insert_path( path, Scratchpad ); + simulation::Region->insert_and_register( path ); } else if( nodedata.type == "traction" ) { @@ -338,7 +338,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad if( false == simulation::Traction.insert( traction ) ) { ErrorLog( "Bad scenario: traction piece with duplicate name \"" + traction->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); } - simulation::Region->insert_traction( traction, Scratchpad ); + simulation::Region->insert_and_register( traction ); } else if( nodedata.type == "tractionpowersource" ) { @@ -367,14 +367,14 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad auto const cellcount = instance->TerrainCount() + 1; // zliczenie submodeli for( auto i = 1; i < cellcount; ++i ) { auto *submodel = instance->TerrainSquare( i - 1 ); - simulation::Region->insert_shape( + simulation::Region->insert( scene::shape_node().convert( submodel ), Scratchpad, false ); // if there's more than one group of triangles in the cell they're held as children of the primary submodel submodel = submodel->ChildGet(); while( submodel != nullptr ) { - simulation::Region->insert_shape( + simulation::Region->insert( scene::shape_node().convert( submodel ), Scratchpad, false ); @@ -398,7 +398,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad if( false == simulation::Instances.insert( instance ) ) { ErrorLog( "Bad scenario: 3d model instance with duplicate name \"" + instance->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); } - simulation::Region->insert_instance( instance, Scratchpad ); + simulation::Region->insert( instance ); } } else if( ( nodedata.type == "triangles" ) @@ -407,7 +407,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad if( false == Scratchpad.binary.terrain ) { - simulation::Region->insert_shape( + simulation::Region->insert( scene::shape_node().import( Input, nodedata ), Scratchpad, @@ -424,7 +424,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad if( false == Scratchpad.binary.terrain ) { - simulation::Region->insert_lines( + simulation::Region->insert( scene::lines_node().import( Input, nodedata ), Scratchpad ); @@ -440,7 +440,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad if( false == simulation::Memory.insert( memorycell ) ) { ErrorLog( "Bad scenario: memory memorycell with duplicate name \"" + memorycell->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); } - simulation::Region->insert_memorycell( memorycell, Scratchpad ); + simulation::Region->insert( memorycell ); } else if( nodedata.type == "eventlauncher" ) { @@ -454,7 +454,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad simulation::Events.queue( eventlauncher ); } else { - simulation::Region->insert_launcher( eventlauncher, Scratchpad ); + simulation::Region->insert( eventlauncher ); } } else if( nodedata.type == "sound" ) { @@ -463,7 +463,7 @@ state_manager::deserialize_node( cParser &Input, scene::scratch_data &Scratchpad if( false == simulation::Sounds.insert( sound ) ) { ErrorLog( "Bad scenario: sound node with duplicate name \"" + sound->name() + "\" encountered in file \"" + Input.Name() + "\" (line " + std::to_string( inputline ) + ")" ); } - simulation::Region->insert_sound( sound, Scratchpad ); + simulation::Region->insert( sound ); } } diff --git a/uart.cpp b/uart.cpp index 2672c6bf..c94f7b57 100644 --- a/uart.cpp +++ b/uart.cpp @@ -208,7 +208,7 @@ void uart_input::poll() double const position { (float)( ( (uint16_t)buffer[ 8 ] | ( (uint16_t)buffer[ 9 ] << 8 ) ) - conf.mainbrakemin ) / ( conf.mainbrakemax - conf.mainbrakemin ) }; relay.post( user_command::trainbrakeset, - reinterpret_cast( position ), + position, 0, GLFW_PRESS, // TODO: pass correct entity id once the missing systems are in place @@ -219,7 +219,7 @@ void uart_input::poll() double const position { (float)( ( (uint16_t)buffer[ 10 ] | ( (uint16_t)buffer[ 11 ] << 8 ) ) - conf.localbrakemin ) / ( conf.localbrakemax - conf.localbrakemin ) }; relay.post( user_command::independentbrakeset, - reinterpret_cast( position ), + position, 0, GLFW_PRESS, // TODO: pass correct entity id once the missing systems are in place diff --git a/uilayer.cpp b/uilayer.cpp index 69fd954c..260710c0 100644 --- a/uilayer.cpp +++ b/uilayer.cpp @@ -109,7 +109,7 @@ ui_layer::on_key( int const Key, int const Action ) { EditorModeFlag = ( Key == GLFW_KEY_F11 ); if( ( true == EditorModeFlag ) && ( false == Global.ControlPicking ) ) { - glfwSetInputMode( m_window, GLFW_CURSOR, GLFW_CURSOR_NORMAL ); + set_cursor( GLFW_CURSOR_NORMAL ); Global.ControlPicking = true; } } @@ -1113,6 +1113,13 @@ ui_layer::render() { glPopAttrib(); } +void +ui_layer::set_cursor( int const Mode ) { + + glfwSetInputMode( m_window, GLFW_CURSOR, Mode ); + m_cursorvisible = ( Mode != GLFW_CURSOR_DISABLED ); +} + void ui_layer::set_progress( float const Progress, float const Subtaskprogress ) { @@ -1214,6 +1221,7 @@ void ui_layer::render_tooltip() { if( m_tooltip.empty() ) { return; } + if( false == m_cursorvisible ) { return; } glm::dvec2 mousepos; glfwGetCursorPos( m_window, &mousepos.x, &mousepos.y ); diff --git a/uilayer.h b/uilayer.h index 7e27aab8..ee6ea8e3 100644 --- a/uilayer.h +++ b/uilayer.h @@ -52,6 +52,9 @@ public: // draws requested UI elements void render(); + // + void + set_cursor( int const Mode ); // stores operation progress void set_progress( float const Progress = 0.0f, float const Subtaskprogress = 0.0f ); @@ -114,6 +117,7 @@ private: int tprev; // poprzedni czas double VelPrev; // poprzednia prędkość double Acc; // przyspieszenie styczne + bool m_cursorvisible { false }; }; extern ui_layer UILayer; diff --git a/utilities.h b/utilities.h index 21dedce9..089c2eb9 100644 --- a/utilities.h +++ b/utilities.h @@ -228,6 +228,13 @@ clamp_circular( Type_ Value, Type_ const Range = static_cast(360) ) { return Value; } +template +Type_ +quantize( Type_ const Value, Type_ const Step ) { + + return ( Step * std::round( Value / Step ) ); +} + template Type_ min_speed( Type_ const Left, Type_ const Right ) {