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 SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
19  *   CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
20  *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21  *   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  *   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23  *   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26  *   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  *   AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  *   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  *   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  *   POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <dart/dart.hpp>
34 #include <dart/external/imgui/imgui.h>
35 #include <dart/gui/osg/osg.hpp>
36 
37 //==============================================================================
38 class CustomWorldNode : public dart::gui::osg::WorldNode
39 {
40 public:
CustomWorldNode(const dart::simulation::WorldPtr & world=nullptr)41   CustomWorldNode(const dart::simulation::WorldPtr& world = nullptr)
42     : dart::gui::osg::WorldNode(world)
43   {
44     // Set up the customized WorldNode
45   }
46 
customPreRefresh()47   void customPreRefresh()
48   {
49     // Use this function to execute custom code before each time that the
50     // window is rendered. This function can be deleted if it does not need
51     // to be used.
52   }
53 
customPostRefresh()54   void customPostRefresh()
55   {
56     // Use this function to execute custom code after each time that the
57     // window is rendered. This function can be deleted if it does not need
58     // to be used.
59   }
60 
customPreStep()61   void customPreStep()
62   {
63     // Use this function to execute custom code before each simulation time
64     // step is performed. This function can be deleted if it does not need
65     // to be used.
66   }
67 
customPostStep()68   void customPostStep()
69   {
70     // Use this function to execute custom code after each simulation time
71     // step is performed. This function can be deleted if it does not need
72     // to be used.
73   }
74 };
75 
76 //==============================================================================
77 class CustomEventHandler : public osgGA::GUIEventHandler
78 {
79 public:
CustomEventHandler()80   CustomEventHandler(/*Pass in any necessary arguments*/)
81   {
82     // Set up the customized event handler
83   }
84 
handle(const osgGA::GUIEventAdapter & ea,osgGA::GUIActionAdapter &)85   virtual bool handle(
86       const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter&) override
87   {
88     if (ea.getEventType() == osgGA::GUIEventAdapter::KEYDOWN)
89     {
90       if (ea.getKey() == 'q')
91       {
92         std::cout << "Lowercase q pressed" << std::endl;
93         return true;
94       }
95       else if (ea.getKey() == 'Q')
96       {
97         std::cout << "Capital Q pressed" << std::endl;
98         return true;
99       }
100       else if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Left)
101       {
102         std::cout << "Left arrow key pressed" << std::endl;
103         return true;
104       }
105       else if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Right)
106       {
107         std::cout << "Right arrow key pressed" << std::endl;
108         return true;
109       }
110     }
111     else if (ea.getEventType() == osgGA::GUIEventAdapter::KEYUP)
112     {
113       if (ea.getKey() == 'q')
114       {
115         std::cout << "Lowercase q released" << std::endl;
116         return true;
117       }
118       else if (ea.getKey() == 'Q')
119       {
120         std::cout << "Capital Q released" << std::endl;
121         return true;
122       }
123       else if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Left)
124       {
125         std::cout << "Left arrow key released" << std::endl;
126         return true;
127       }
128       else if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Right)
129       {
130         std::cout << "Right arrow key released" << std::endl;
131         return true;
132       }
133     }
134 
135     // The return value should be 'true' if the input has been fully handled
136     // and should not be visible to any remaining event handlers. It should be
137     // false if the input has not been fully handled and should be viewed by
138     // any remaining event handlers.
139     return false;
140   }
141 };
142 
143 //==============================================================================
144 class TestWidget : public dart::gui::osg::ImGuiWidget
145 {
146 public:
147   /// Constructor
TestWidget(dart::gui::osg::ImGuiViewer * viewer,dart::simulation::WorldPtr world)148   TestWidget(
149       dart::gui::osg::ImGuiViewer* viewer, dart::simulation::WorldPtr world)
150     : mViewer(viewer),
151       mWorld(std::move(world)),
152       mGuiGravity(true),
153       mGravity(true),
154       mGuiHeadlights(true)
155   {
156     // Do nothing
157   }
158 
159   // Documentation inherited
render()160   void render() override
161   {
162     ImGui::SetNextWindowPos(ImVec2(10, 20));
163     ImGui::SetNextWindowSize(ImVec2(240, 320));
164     ImGui::SetNextWindowBgAlpha(0.5f);
165     if (!ImGui::Begin(
166             "Tinkertoy Control",
167             nullptr,
168             ImGuiWindowFlags_NoResize | ImGuiWindowFlags_MenuBar
169                 | ImGuiWindowFlags_HorizontalScrollbar))
170     {
171       // Early out if the window is collapsed, as an optimization.
172       ImGui::End();
173       return;
174     }
175 
176     // Menu
177     if (ImGui::BeginMenuBar())
178     {
179       if (ImGui::BeginMenu("Menu"))
180       {
181         if (ImGui::MenuItem("Exit"))
182           mViewer->setDone(true);
183         ImGui::EndMenu();
184       }
185       if (ImGui::BeginMenu("Help"))
186       {
187         if (ImGui::MenuItem("About DART"))
188           mViewer->showAbout();
189         ImGui::EndMenu();
190       }
191       ImGui::EndMenuBar();
192     }
193 
194     ImGui::Text("An empty OSG example with ImGui");
195     ImGui::Spacing();
196 
197     if (ImGui::CollapsingHeader("Simulation", ImGuiTreeNodeFlags_DefaultOpen))
198     {
199       int e = mViewer->isSimulating() ? 0 : 1;
200       if (mViewer->isAllowingSimulation())
201       {
202         if (ImGui::RadioButton("Play", &e, 0) && !mViewer->isSimulating())
203           mViewer->simulate(true);
204         ImGui::SameLine();
205         if (ImGui::RadioButton("Pause", &e, 1) && mViewer->isSimulating())
206           mViewer->simulate(false);
207       }
208 
209       ImGui::Text("Time: %.3f", mWorld->getTime());
210     }
211 
212     if (ImGui::CollapsingHeader(
213             "World Options", ImGuiTreeNodeFlags_DefaultOpen))
214     {
215       // Gravity
216       ImGui::Checkbox("Gravity On/Off", &mGuiGravity);
217       setGravity(mGuiGravity);
218 
219       ImGui::Spacing();
220 
221       // Headlights
222       mGuiHeadlights = mViewer->checkHeadlights();
223       ImGui::Checkbox("Headlights On/Off", &mGuiHeadlights);
224       mViewer->switchHeadlights(mGuiHeadlights);
225     }
226 
227     if (ImGui::CollapsingHeader("View", ImGuiTreeNodeFlags_DefaultOpen))
228     {
229       osg::Vec3d eye;
230       osg::Vec3d center;
231       osg::Vec3d up;
232       mViewer->getCamera()->getViewMatrixAsLookAt(eye, center, up);
233 
234       ImGui::Text("Eye   : (%.2f, %.2f, %.2f)", eye.x(), eye.y(), eye.z());
235       ImGui::Text(
236           "Center: (%.2f, %.2f, %.2f)", center.x(), center.y(), center.z());
237       ImGui::Text("Up    : (%.2f, %.2f, %.2f)", up.x(), up.y(), up.z());
238     }
239 
240     if (ImGui::CollapsingHeader("Help"))
241     {
242       ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + 320);
243       ImGui::Text("User Guide:\n");
244       ImGui::Text("%s", mViewer->getInstructions().c_str());
245       ImGui::PopTextWrapPos();
246     }
247 
248     ImGui::End();
249   }
250 
251 protected:
setGravity(bool gravity)252   void setGravity(bool gravity)
253   {
254     if (mGravity == gravity)
255       return;
256 
257     mGravity = gravity;
258 
259     if (mGravity)
260       mWorld->setGravity(-9.81 * Eigen::Vector3d::UnitZ());
261     else
262       mWorld->setGravity(Eigen::Vector3d::Zero());
263   }
264 
265   osg::ref_ptr<dart::gui::osg::ImGuiViewer> mViewer;
266   dart::simulation::WorldPtr mWorld;
267   bool mGuiGravity;
268   bool mGravity;
269   bool mGuiHeadlights;
270 };
271 
272 //==============================================================================
main()273 int main()
274 {
275   // Create a world
276   dart::simulation::WorldPtr world(new dart::simulation::World);
277 
278   // Add a target object to the world
279   dart::gui::osg::InteractiveFramePtr target(
280       new dart::gui::osg::InteractiveFrame(dart::dynamics::Frame::World()));
281   world->addSimpleFrame(target);
282 
283   // Wrap a WorldNode around it
284   osg::ref_ptr<CustomWorldNode> node = new CustomWorldNode(world);
285 
286   // Create a Viewer and set it up with the WorldNode
287   osg::ref_ptr<dart::gui::osg::ImGuiViewer> viewer
288       = new dart::gui::osg::ImGuiViewer();
289   viewer->addWorldNode(node);
290 
291   // Add control widget for atlas
292   viewer->getImGuiHandler()->addWidget(
293       std::make_shared<TestWidget>(viewer, world));
294 
295   // Active the drag-and-drop feature for the target
296   viewer->enableDragAndDrop(target.get());
297 
298   // Pass in the custom event handler
299   viewer->addEventHandler(new CustomEventHandler);
300 
301   // Set up the window to be 640x480
302   viewer->setUpViewInWindow(0, 0, 640, 480);
303 
304   // Adjust the viewpoint of the Viewer
305   viewer->getCameraManipulator()->setHomePosition(
306       ::osg::Vec3(2.57f, 3.14f, 1.64f),
307       ::osg::Vec3(0.00f, 0.00f, 0.00f),
308       ::osg::Vec3(-0.24f, -0.25f, 0.94f));
309   // We need to re-dirty the CameraManipulator by passing it into the viewer
310   // again, so that the viewer knows to update its HomePosition setting
311   viewer->setCameraManipulator(viewer->getCameraManipulator());
312 
313   // Begin running the application loop
314   viewer->run();
315 }
316