From cbc9b58beb107db7cddcbcf369b120a5d41aad3e Mon Sep 17 00:00:00 2001 From: Mathieu Westphal Date: Sun, 12 Nov 2023 11:11:32 +0100 Subject: [PATCH] Introduce a new interaction API --- application/F3DStarter.cxx | 1 + library/private/interactor_impl.h | 3 ++ library/public/interactor.h | 35 +++++++++++++ library/src/interactor.cxx | 28 +++++++++++ library/src/interactor_impl.cxx | 83 +++++++++++++++++++++++++++---- 5 files changed, 140 insertions(+), 10 deletions(-) diff --git a/application/F3DStarter.cxx b/application/F3DStarter.cxx index 0d5f14af67..3aee4fa03b 100644 --- a/application/F3DStarter.cxx +++ b/application/F3DStarter.cxx @@ -186,6 +186,7 @@ int F3DStarter::Start(int argc, char** argv) f3d::window& window = this->Internals->Engine->getWindow(); f3d::interactor& interactor = this->Internals->Engine->getInteractor(); + interactor.addDefaultKeyPressInteractions(); // TODO where should we put this ? interactor.setKeyPressCallBack( [this](int, const std::string& keySym) -> bool { diff --git a/library/private/interactor_impl.h b/library/private/interactor_impl.h index 21833578b7..08115e1c27 100644 --- a/library/private/interactor_impl.h +++ b/library/private/interactor_impl.h @@ -35,6 +35,9 @@ class interactor_impl : public interactor interactor_impl(options& options, window_impl& window, loader_impl& loader); ~interactor_impl() override; + interactor& addKeyPressCallBack(const std::string& keySym, ModifierKeys modifiers, std::function callBack) override; + interactor& addKeyPressToggle(const std::string& keySym, ModifierKeys modifiers, const std::string& option) override; + interactor& setKeyPressCallBack(std::function callBack) override; interactor& setDropFilesCallBack(std::function)> callBack) override; diff --git a/library/public/interactor.h b/library/public/interactor.h index d127103b9d..cbf0d31d4f 100644 --- a/library/public/interactor.h +++ b/library/public/interactor.h @@ -23,6 +23,41 @@ namespace f3d class F3D_EXPORT interactor { public: + + /** + * Enumeration of supported modifier combination, in binary. + */ + enum class ModifierKeys : unsigned char + { + ANY = 0x80, // 10000000 + NONE = 0x0, // 00000000 + CTRL = 0x1, // 00000001 + SHIFT = 0x2, // 00000010 + CTRL_SHIFT = 0x3 // 00000011 + }; + + /** + * Use this method to specify your own keypress callback for a specified keysym and modifiers flag. + * keysym being the pressed key symbol, TODO list ? + * modifiers is a binary flag from the dedicated enum. + * Adding a callback for same keySym and modifiers as any existing key press interaction will remove the previous one. + * Expected API: + * \code + * void callBack() + * \endcode + */ + virtual interactor& addKeyPressCallBack(const std::string& keySym, ModifierKeys modifiers, std::function callBack) = 0; + + /** + * Use this method to specify your to toggle a boolean option for a specified keysym and modifiers flag. + * keysym being the pressed key symbol, TODO list ? + * modifiers is a binary flag from the dedicated enum. + * Adding a toggle for same keySym and modifiers as any existing key press interaction will remove the previous one. + */ + virtual interactor& addKeyPressToggle(const std::string& keySym, ModifierKeys modifiers, const std::string& option) = 0; + + virtual interactor& addDefaultKeyPressInteractions(); + /** * Use this method to specify your own keypress callback, with the expected API: * \code diff --git a/library/src/interactor.cxx b/library/src/interactor.cxx index 5d59b74c26..f96f7f059d 100644 --- a/library/src/interactor.cxx +++ b/library/src/interactor.cxx @@ -2,6 +2,34 @@ namespace f3d { + +//---------------------------------------------------------------------------- +interactor& interactor::addDefaultKeyPressInteractions() +{ + this->addKeyPressToggle("b", ModifierKeys::ANY, "ui.bar"); + this->addKeyPressToggle("p", ModifierKeys::ANY, "render.effect.translucency-support"); + this->addKeyPressToggle("q", ModifierKeys::ANY, "render.effect.ambient-occlusion"); + this->addKeyPressToggle("a", ModifierKeys::ANY, "render.effect.anti-aliasing"); + this->addKeyPressToggle("t", ModifierKeys::ANY, "render.effect.tone-mapping"); + this->addKeyPressToggle("e", ModifierKeys::ANY, "render.show-edges"); + this->addKeyPressToggle("x", ModifierKeys::ANY, "interactor.axis"); + this->addKeyPressToggle("g", ModifierKeys::ANY, "render.grid.enable"); + this->addKeyPressToggle("n", ModifierKeys::ANY, "ui.filename"); + this->addKeyPressToggle("m", ModifierKeys::ANY, "ui.metadata"); + this->addKeyPressToggle("r", ModifierKeys::ANY, "render.raytracing.enable"); + this->addKeyPressToggle("d", ModifierKeys::ANY, "render.raytracing.denoise"); + this->addKeyPressToggle("v", ModifierKeys::ANY, "model.volume.enable"); + this->addKeyPressToggle("i", ModifierKeys::ANY, "model.volume.inverse"); + this->addKeyPressToggle("o", ModifierKeys::ANY, "model.point-sprites.enable"); + this->addKeyPressToggle("u", ModifierKeys::ANY, "render.background.blur"); + this->addKeyPressToggle("k", ModifierKeys::ANY, "interactor.trackball"); + this->addKeyPressToggle("f", ModifierKeys::ANY, "render.hdri.ambient"); + this->addKeyPressToggle("f", ModifierKeys::ANY, "render.background.skybox"); + this->addKeyPressToggle("h", ModifierKeys::ANY, "ui.cheatsheet"); + return *this; +} + +//---------------------------------------------------------------------------- const std::vector >& interactor::getDefaultInteractionsInfo() { // clang-format off diff --git a/library/src/interactor_impl.cxx b/library/src/interactor_impl.cxx index 1ff2f7905d..c11785d9e0 100644 --- a/library/src/interactor_impl.cxx +++ b/library/src/interactor_impl.cxx @@ -147,19 +147,58 @@ class interactor_impl::internals internals* self = static_cast(clientData); vtkRenderWindowInteractor* rwi = self->Style->GetInteractor(); - int keyCode = std::toupper(rwi->GetKeyCode()); std::string keySym = rwi->GetKeySym(); - if (keySym.length() > 0) + int shift = rwi->GetShiftKey(); + int ctrl = rwi->GetControlKey(); + + // Retrocompatible support of the KeyPressUserCallBack + int keyCode = std::toupper(rwi->GetKeyCode()); + std::string upKeySym = keySym; + if (upKeySym.length() > 0) { // Make sure key symbols starts with an upper char (e.g. "space") - keySym[0] = std::toupper(keySym[0]); + upKeySym[0] = std::toupper(upKeySym[0]); } - if (self->KeyPressUserCallBack(keyCode, keySym)) + if (self->KeyPressUserCallBack(keyCode, upKeySym)) { return; } + // Actual key press implementation + // Check of an *any modifier* interaction first + auto mapKey = std::make_pair(keySym, ModifierKeys::ANY); + auto callBack = self->KeyCallBacks.find(mapKey); + if (callBack != self->KeyCallBacks.end()) + { + callBack->second(); + } + else + { + // Check for an interaction callBack with modifiers + ModifierKeys modifiers = ModifierKeys::NONE; + if (shift == 1 && ctrl == 1) + { + modifiers = ModifierKeys::CTRL_SHIFT; + } + else if (ctrl == 1) + { + modifiers = ModifierKeys::CTRL; + } + else if (shift == 1) + { + modifiers = ModifierKeys::SHIFT; + } + mapKey.second = modifiers; + + callBack = self->KeyCallBacks.find(mapKey); + if (callBack != self->KeyCallBacks.end()) + { + callBack->second(); + } + } + + // TODO remove old code below // No user defined behavior, use standard behavior vtkRenderWindow* renWin = self->Window.GetRenderWindow(); vtkF3DRenderer* ren = vtkF3DRenderer::SafeDownCast(renWin->GetRenderers()->GetFirstRenderer()); @@ -197,6 +236,7 @@ class interactor_impl::internals render = true; } break; +/* case 'B': self->Options.toggle("ui.bar"); render = true; @@ -237,13 +277,15 @@ class interactor_impl::internals self->Options.toggle("ui.metadata"); render = true; break; +*/ case 'Z': self->Options.toggle("ui.fps"); self->Window.render(); self->Window.render(); // XXX: Double render is needed here break; - case 'R': +/* + case 'R': self->Options.toggle("render.raytracing.enable"); render = true; break; @@ -279,6 +321,7 @@ class interactor_impl::internals self->Options.toggle("render.background.skybox"); render = true; break; + */ case 'L': { const double intensity = self->Options.getAsDouble("render.light.intensity"); @@ -303,10 +346,10 @@ class interactor_impl::internals render = true; break; } - case 'H': +/* case 'H': self->Options.toggle("ui.cheatsheet"); render = true; - break; + break;*/ case '?': self->Window.PrintColoringDescription(log::VerboseLevel::INFO); self->Window.PrintSceneDescription(log::VerboseLevel::INFO); @@ -328,16 +371,16 @@ class interactor_impl::internals render = true; break; default: - if (keySym == F3D_EXIT_HOTKEY_SYM) + if (upKeySym == F3D_EXIT_HOTKEY_SYM) { self->StopInteractor(); } - else if (keySym == "Return") + else if (upKeySym == "Return") { self->Window.getCamera().resetToDefault(); render = true; } - else if (keySym == "Space") + else if (upKeySym == "Space") { assert(self->AnimationManager); self->AnimationManager->ToggleAnimation(); @@ -541,6 +584,7 @@ class interactor_impl::internals vtkNew Style; vtkSmartPointer Recorder; std::map > > TimerCallBacks; + std::map, std::function > KeyCallBacks; vtkNew CellPicker; vtkNew PointPicker; @@ -562,6 +606,25 @@ interactor_impl::interactor_impl(options& options, window_impl& window, loader_i //---------------------------------------------------------------------------- interactor_impl::~interactor_impl() = default; +//---------------------------------------------------------------------------- +interactor& interactor_impl::addKeyPressCallBack(const std::string& keySym, ModifierKeys modifiers, std::function callBack) +{ + this->Internals->KeyCallBacks[std::make_pair(keySym, modifiers)] = callBack; + return *this; +} + +//---------------------------------------------------------------------------- +interactor& interactor_impl::addKeyPressToggle(const std::string& keySym, ModifierKeys modifiers, const std::string& option) +{ + this->addKeyPressCallBack(keySym, modifiers, + [this, option]() -> void + { + this->Internals->Options.toggle(option); + this->Internals->Window.render(); + }); + return *this; +} + //---------------------------------------------------------------------------- interactor& interactor_impl::setKeyPressCallBack(std::function callBack) {