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