1 /*
2  * Copyright (c) 2011-2021, The DART development contributors
3  * All rights reserved.
4  *
5  * The list of contributors can be found at:
6  *   https://github.com/dartsim/dart/blob/master/LICENSE
7  *
8  * This file is provided under the following "BSD-style" License:
9  *   Redistribution and use in source and binary forms, with or
10  *   without modification, are permitted provided that the following
11  *   conditions are met:
12  *   * Redistributions of source code must retain the above copyright
13  *     notice, this list of conditions and the following disclaimer.
14  *   * Redistributions in binary form must reproduce the above
15  *     copyright notice, this list of conditions and the following
16  *     disclaimer in the documentation and/or other materials provided
17  *     with the distribution.
18  *   * This code incorporates portions of Open Dynamics Engine
19  *     (Copyright (c) 2001-2004, Russell L. Smith. All rights
20  *     reserved.) and portions of FCL (Copyright (c) 2011, Willow
21  *     Garage, Inc. All rights reserved.), which were released under
22  *     the same BSD license as below
23  *
24  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
25  *   CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
26  *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
27  *   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28  *   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
29  *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
32  *   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
33  *   AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  *   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35  *   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  *   POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 #include "dart/gui/osg/ImGuiHandler.hpp"
40 
41 #include <algorithm>
42 
43 #include <osg/Camera>
44 #include <osg/RenderInfo>
45 
46 #include "dart/external/imgui/imgui.h"
47 #include "dart/external/imgui/imgui_impl_opengl2.h"
48 
49 #include "dart/common/Console.hpp"
50 #include "dart/gui/osg/ImGuiWidget.hpp"
51 
52 namespace dart {
53 namespace gui {
54 namespace osg {
55 
56 //==============================================================================
57 // Special keys that are usually greater than 512 in osgGA
58 //
59 // Imporant Note: Dear ImGui expects the control Keys indices not to be greater
60 // thant 511. It actually uses an array of 512 elements. However, OSG has
61 // indices greater than that. So here I do a conversion for special keys between
62 // ImGui and OSG.
63 enum ConvertedKey : int
64 {
65   ConvertedKey_Tab = 257,
66   ConvertedKey_Left,
67   ConvertedKey_Right,
68   ConvertedKey_Up,
69   ConvertedKey_Down,
70   ConvertedKey_PageUp,
71   ConvertedKey_PageDown,
72   ConvertedKey_Home,
73   ConvertedKey_End,
74   ConvertedKey_Delete,
75   ConvertedKey_BackSpace,
76   ConvertedKey_Enter,
77   ConvertedKey_Escape,
78   ConvertedKey_LeftControl,
79   ConvertedKey_RightControl,
80   ConvertedKey_LeftShift,
81   ConvertedKey_RightShift,
82   ConvertedKey_LeftAlt,
83   ConvertedKey_RightAlt,
84   ConvertedKey_LeftSuper,
85   ConvertedKey_RightSuper,
86 };
87 
88 //==============================================================================
89 // Check for a special key and return the converted code (range [257, 511]) if
90 // so. Otherwise returns -1
convertFromOSGKey(int key)91 int convertFromOSGKey(int key)
92 {
93   using KeySymbol = osgGA::GUIEventAdapter::KeySymbol;
94 
95   switch (key)
96   {
97     case KeySymbol::KEY_Tab:
98       return ConvertedKey_Tab;
99     case KeySymbol::KEY_Left:
100       return ConvertedKey_Left;
101     case KeySymbol::KEY_Right:
102       return ConvertedKey_Right;
103     case KeySymbol::KEY_Up:
104       return ConvertedKey_Up;
105     case KeySymbol::KEY_Down:
106       return ConvertedKey_Down;
107     case KeySymbol::KEY_Page_Up:
108       return ConvertedKey_PageUp;
109     case KeySymbol::KEY_Page_Down:
110       return ConvertedKey_PageDown;
111     case KeySymbol::KEY_Home:
112       return ConvertedKey_Home;
113     case KeySymbol::KEY_End:
114       return ConvertedKey_End;
115     case KeySymbol::KEY_Delete:
116       return ConvertedKey_Delete;
117     case KeySymbol::KEY_BackSpace:
118       return ConvertedKey_BackSpace;
119     case KeySymbol::KEY_Return:
120       return ConvertedKey_Enter;
121     case KeySymbol::KEY_Escape:
122       return ConvertedKey_Escape;
123     case KeySymbol::KEY_Control_L:
124       return ConvertedKey_LeftControl;
125     case KeySymbol::KEY_Control_R:
126       return ConvertedKey_RightControl;
127     case KeySymbol::KEY_Shift_L:
128       return ConvertedKey_LeftShift;
129     case KeySymbol::KEY_Shift_R:
130       return ConvertedKey_RightShift;
131     case KeySymbol::KEY_Alt_L:
132       return ConvertedKey_LeftAlt;
133     case KeySymbol::KEY_Alt_R:
134       return ConvertedKey_RightAlt;
135     case KeySymbol::KEY_Super_L:
136       return ConvertedKey_LeftSuper;
137     case KeySymbol::KEY_Super_R:
138       return ConvertedKey_RightSuper;
139     default:
140       return -1;
141   }
142 }
143 
144 //==============================================================================
145 struct ImGuiNewFrameCallback : public ::osg::Camera::DrawCallback
146 {
ImGuiNewFrameCallbackdart::gui::osg::ImGuiNewFrameCallback147   ImGuiNewFrameCallback(ImGuiHandler* handler) : mHandler(handler)
148   {
149     // Do nothing
150   }
151 
operator ()dart::gui::osg::ImGuiNewFrameCallback152   virtual void operator()(::osg::RenderInfo& renderInfo) const
153   {
154     mHandler->newFrame(renderInfo);
155   }
156 
157 private:
158   ::osg::ref_ptr<ImGuiHandler> mHandler;
159 };
160 
161 //==============================================================================
162 struct ImGuiDrawCallback : public ::osg::Camera::DrawCallback
163 {
ImGuiDrawCallbackdart::gui::osg::ImGuiDrawCallback164   ImGuiDrawCallback(ImGuiHandler* handler) : mHandler(handler)
165   {
166     // Do nothing
167   }
168 
operator ()dart::gui::osg::ImGuiDrawCallback169   virtual void operator()(::osg::RenderInfo& renderInfo) const
170   {
171     mHandler->render(renderInfo);
172   }
173 
174 private:
175   ::osg::ref_ptr<ImGuiHandler> mHandler;
176 };
177 
178 //==============================================================================
ImGuiHandler()179 ImGuiHandler::ImGuiHandler()
180   : mTime{0.0}, mMousePressed{false, false, false}, mMouseWheel{0.0f}
181 {
182   ImGui::CreateContext();
183 
184   ImGui::StyleColorsDark();
185 
186   ImGui_ImplOpenGL2_Init();
187 
188   // Keyboard mapping. ImGui will use those indices to peek into the
189   // io.KeyDown[] array.
190   ImGuiIO& io = ImGui::GetIO();
191   io.KeyMap[ImGuiKey_Tab] = ConvertedKey_Tab;
192   io.KeyMap[ImGuiKey_LeftArrow] = ConvertedKey_Left;
193   io.KeyMap[ImGuiKey_RightArrow] = ConvertedKey_Right;
194   io.KeyMap[ImGuiKey_UpArrow] = ConvertedKey_Up;
195   io.KeyMap[ImGuiKey_DownArrow] = ConvertedKey_Down;
196   io.KeyMap[ImGuiKey_PageUp] = ConvertedKey_PageUp;
197   io.KeyMap[ImGuiKey_PageDown] = ConvertedKey_PageDown;
198   io.KeyMap[ImGuiKey_Home] = ConvertedKey_Home;
199   io.KeyMap[ImGuiKey_End] = ConvertedKey_End;
200   io.KeyMap[ImGuiKey_Delete] = ConvertedKey_Delete;
201   io.KeyMap[ImGuiKey_Backspace] = ConvertedKey_BackSpace;
202   io.KeyMap[ImGuiKey_Enter] = ConvertedKey_Enter;
203   io.KeyMap[ImGuiKey_Escape] = ConvertedKey_Escape;
204   io.KeyMap[ImGuiKey_A] = osgGA::GUIEventAdapter::KeySymbol::KEY_A;
205   io.KeyMap[ImGuiKey_C] = osgGA::GUIEventAdapter::KeySymbol::KEY_C;
206   io.KeyMap[ImGuiKey_V] = osgGA::GUIEventAdapter::KeySymbol::KEY_V;
207   io.KeyMap[ImGuiKey_X] = osgGA::GUIEventAdapter::KeySymbol::KEY_X;
208   io.KeyMap[ImGuiKey_Y] = osgGA::GUIEventAdapter::KeySymbol::KEY_Y;
209   io.KeyMap[ImGuiKey_Z] = osgGA::GUIEventAdapter::KeySymbol::KEY_Z;
210 }
211 
212 //==============================================================================
~ImGuiHandler()213 ImGuiHandler::~ImGuiHandler()
214 {
215   // Do nothing
216 }
217 
218 //==============================================================================
setCameraCallbacks(::osg::Camera * camera)219 void ImGuiHandler::setCameraCallbacks(::osg::Camera* camera)
220 {
221   if (nullptr == camera)
222     return;
223 
224   ImGuiDrawCallback* postDrawCallback = new ImGuiDrawCallback(this);
225   camera->setPostDrawCallback(postDrawCallback);
226 
227   ImGuiNewFrameCallback* preDrawCallback = new ImGuiNewFrameCallback(this);
228   camera->setPreDrawCallback(preDrawCallback);
229 }
230 
231 //==============================================================================
hasWidget(const std::shared_ptr<ImGuiWidget> & widget) const232 bool ImGuiHandler::hasWidget(const std::shared_ptr<ImGuiWidget>& widget) const
233 {
234   return std::find(mWidgets.begin(), mWidgets.end(), widget) != mWidgets.end();
235 }
236 
237 //==============================================================================
addWidget(const std::shared_ptr<ImGuiWidget> & widget,bool visible)238 void ImGuiHandler::addWidget(
239     const std::shared_ptr<ImGuiWidget>& widget, bool visible)
240 {
241   if (hasWidget(widget))
242   {
243     dtwarn
244         << "[ImGuiHandler::addWidget] Attempting to add existing widget to the "
245            "viewer. Ignoring this action.";
246     return;
247   }
248 
249   widget->setVisible(visible);
250   mWidgets.push_back(widget);
251 }
252 
253 //==============================================================================
removeWidget(const std::shared_ptr<ImGuiWidget> & widget)254 void ImGuiHandler::removeWidget(const std::shared_ptr<ImGuiWidget>& widget)
255 {
256   if (!hasWidget(widget))
257   {
258     dtwarn << "[ImGuiHandler::removeWidget] Attempting to remove not existing "
259               "widget from the viewer. Ignoring this action.\n";
260     return;
261   }
262 
263   mWidgets.erase(
264       std::remove(mWidgets.begin(), mWidgets.end(), widget), mWidgets.end());
265 }
266 
267 //==============================================================================
removeAllWidget()268 void ImGuiHandler::removeAllWidget()
269 {
270   mWidgets.clear();
271 }
272 
273 //==============================================================================
handle(const osgGA::GUIEventAdapter & eventAdapter,osgGA::GUIActionAdapter &,::osg::Object *,::osg::NodeVisitor *)274 bool ImGuiHandler::handle(
275     const osgGA::GUIEventAdapter& eventAdapter,
276     osgGA::GUIActionAdapter& /*actionAdapter*/,
277     ::osg::Object* /*object*/,
278     ::osg::NodeVisitor* /*nodeVisitor*/)
279 {
280   auto& io = ImGui::GetIO();
281   const auto wantCapureMouse = io.WantCaptureMouse;
282   const auto wantCapureKeyboard = io.WantCaptureKeyboard;
283 
284   switch (eventAdapter.getEventType())
285   {
286     case osgGA::GUIEventAdapter::KEYDOWN:
287     {
288       const auto c = eventAdapter.getUnmodifiedKey();
289       const auto special_key = convertFromOSGKey(c);
290 
291       if (special_key > 0)
292       {
293         assert(special_key < 512 && "ImGui KeysDown is an array of 512");
294         assert(
295             special_key > 256
296             && "ASCII stop at 127, but we use the range [257, 511]");
297 
298         io.KeysDown[special_key] = true;
299 
300         io.KeyCtrl = io.KeysDown[ConvertedKey_LeftControl]
301                      || io.KeysDown[ConvertedKey_RightControl];
302         io.KeyShift = io.KeysDown[ConvertedKey_LeftShift]
303                       || io.KeysDown[ConvertedKey_RightShift];
304         io.KeyAlt = io.KeysDown[ConvertedKey_LeftAlt]
305                     || io.KeysDown[ConvertedKey_RightAlt];
306         io.KeySuper = io.KeysDown[ConvertedKey_LeftSuper]
307                       || io.KeysDown[ConvertedKey_RightSuper];
308       }
309       else if (0 < c && c < 0x10000)
310       {
311         io.KeysDown[c] = true;
312         io.AddInputCharacter(static_cast<ImWchar>(c));
313       }
314 
315       return wantCapureKeyboard;
316     }
317     case osgGA::GUIEventAdapter::KEYUP:
318     {
319       const auto c = eventAdapter.getUnmodifiedKey();
320       const auto special_key = convertFromOSGKey(c);
321 
322       if (special_key > 0)
323       {
324         assert(special_key < 512 && "ImGui KeysDown is an array of 512");
325         assert(
326             special_key > 256
327             && "ASCII stop at 127, but we use the range [257, 511]");
328 
329         io.KeysDown[special_key] = false;
330 
331         io.KeyCtrl = io.KeysDown[ConvertedKey_LeftControl]
332                      || io.KeysDown[ConvertedKey_RightControl];
333         io.KeyShift = io.KeysDown[ConvertedKey_LeftShift]
334                       || io.KeysDown[ConvertedKey_RightShift];
335         io.KeyAlt = io.KeysDown[ConvertedKey_LeftAlt]
336                     || io.KeysDown[ConvertedKey_RightAlt];
337         io.KeySuper = io.KeysDown[ConvertedKey_LeftSuper]
338                       || io.KeysDown[ConvertedKey_RightSuper];
339       }
340       else if (0 < c && c < 0x10000)
341       {
342         io.KeysDown[c] = false;
343         io.AddInputCharacter(static_cast<ImWchar>(c));
344       }
345 
346       return wantCapureKeyboard;
347     }
348     case osgGA::GUIEventAdapter::PUSH:
349     {
350       io.MousePos
351           = ImVec2(eventAdapter.getX(), io.DisplaySize.y - eventAdapter.getY());
352 
353       if (eventAdapter.getButtonMask()
354           == osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON)
355       {
356         mMousePressed[0] = true;
357       }
358       else if (
359           eventAdapter.getButtonMask()
360           == osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON)
361       {
362         mMousePressed[1] = true;
363       }
364       else if (
365           eventAdapter.getButtonMask()
366           == osgGA::GUIEventAdapter::MIDDLE_MOUSE_BUTTON)
367       {
368         mMousePressed[2] = true;
369       }
370       else
371       {
372         // Shouldn't be reached to here. Mark left button by default.
373         mMousePressed[0] = true;
374       }
375 
376       return wantCapureMouse;
377     }
378     case osgGA::GUIEventAdapter::DRAG:
379     case osgGA::GUIEventAdapter::MOVE:
380     {
381       io.MousePos
382           = ImVec2(eventAdapter.getX(), io.DisplaySize.y - eventAdapter.getY());
383 
384       return wantCapureMouse;
385     }
386     case osgGA::GUIEventAdapter::RELEASE:
387     {
388       // When a mouse button is released no button mask is set. So we mark all
389       // the buttons are released.
390       mMousePressed[0] = false;
391       mMousePressed[1] = false;
392       mMousePressed[2] = false;
393 
394       return wantCapureMouse;
395     }
396     case osgGA::GUIEventAdapter::SCROLL:
397     {
398       constexpr float increment = 0.1f;
399 
400       switch (eventAdapter.getScrollingMotion())
401       {
402         case (osgGA::GUIEventAdapter::SCROLL_NONE):
403           break;
404         case (osgGA::GUIEventAdapter::SCROLL_LEFT):
405           break;
406         case (osgGA::GUIEventAdapter::SCROLL_RIGHT):
407           break;
408         case (osgGA::GUIEventAdapter::SCROLL_UP):
409           mMouseWheel += increment;
410           break;
411         case (osgGA::GUIEventAdapter::SCROLL_DOWN):
412           mMouseWheel -= increment;
413           break;
414         case (osgGA::GUIEventAdapter::SCROLL_2D):
415           mMouseWheel = eventAdapter.getScrollingDeltaY();
416           break;
417       }
418 
419       return wantCapureMouse;
420     }
421     default:
422     {
423       return false;
424     }
425   }
426 }
427 
428 //==============================================================================
newFrame(::osg::RenderInfo & renderInfo)429 void ImGuiHandler::newFrame(::osg::RenderInfo& renderInfo)
430 {
431   ImGui_ImplOpenGL2_NewFrame();
432 
433   auto& io = ImGui::GetIO();
434   auto* viewport = renderInfo.getCurrentCamera()->getViewport();
435 
436   io.DisplaySize = ImVec2(viewport->width(), viewport->height());
437 
438   const auto currentTime
439       = renderInfo.getView()->getFrameStamp()->getSimulationTime();
440 
441   io.DeltaTime
442       = mTime > 0.0 ? static_cast<float>(currentTime - mTime) : 1.0f / 60.0f;
443   io.DeltaTime = std::max(io.DeltaTime, std::numeric_limits<float>::min());
444   mTime = currentTime;
445   assert(mTime >= 0.0);
446 
447   for (auto i = 0u; i < mMousePressed.size(); ++i)
448     io.MouseDown[i] = mMousePressed[i];
449 
450   io.MouseWheel = mMouseWheel;
451   mMouseWheel = 0.0f;
452 
453   ImGui::NewFrame();
454 }
455 
456 //==============================================================================
render(::osg::RenderInfo &)457 void ImGuiHandler::render(::osg::RenderInfo& /*renderInfo*/)
458 {
459   for (const auto& widget : mWidgets)
460   {
461     if (widget->isVisible())
462       widget->render();
463   }
464 
465   ImGui::Render();
466 
467   auto* drawData = ImGui::GetDrawData();
468   ImGui_ImplOpenGL2_RenderDrawData(drawData);
469 }
470 
471 } // namespace osg
472 } // namespace gui
473 } // namespace dart
474