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