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/gui/glut/Window.hpp"
34 
35 #include "dart/external/lodepng/lodepng.h"
36 
37 #ifdef _WIN32
38 #  include <direct.h>
39 #  include <sys/stat.h>
40 #  include <sys/types.h>
41 #else
42 #  include <dirent.h>
43 #  include <sys/stat.h>
44 #  include <sys/types.h>
45 #endif
46 #include <cstdio>
47 #include <iostream>
48 #include <vector>
49 
50 #include "dart/common/Console.hpp"
51 #include "dart/gui/GLFuncs.hpp"
52 #include "dart/gui/OpenGLRenderInterface.hpp"
53 #include "dart/gui/glut/LoadGlut.hpp"
54 
55 namespace dart {
56 namespace gui {
57 namespace glut {
58 
59 std::vector<Window*> Window::mWindows;
60 std::vector<int> Window::mWinIDs;
61 
Window()62 Window::Window()
63 {
64   mWinWidth = 0;
65   mWinHeight = 0;
66   mMouseX = 0;
67   mMouseY = 0;
68   mDisplayTimeout = 1000.0 / 30.0;
69   mMouseDown = false;
70   mMouseDrag = false;
71   mCapture = false;
72   mBackground[0] = 0.3;
73   mBackground[1] = 0.3;
74   mBackground[2] = 0.3;
75   mBackground[3] = 1.0;
76   mRI = nullptr;
77 }
78 
~Window()79 Window::~Window()
80 {
81   delete mRI;
82 }
83 
initWindow(int _w,int _h,const char * _name)84 void Window::initWindow(int _w, int _h, const char* _name)
85 {
86   mWindows.push_back(this);
87 
88   mWinWidth = _w;
89   mWinHeight = _h;
90 
91   glutInitDisplayMode(
92       GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA | GLUT_MULTISAMPLE | GLUT_ACCUM);
93   glutInitWindowPosition(150, 100);
94   glutInitWindowSize(_w, _h);
95   mWinIDs.push_back(glutCreateWindow(_name));
96 
97   glutDisplayFunc(refresh);
98   glutReshapeFunc(reshape);
99   glutKeyboardFunc(keyEvent);
100   glutSpecialFunc(specKeyEvent);
101   glutMouseFunc(mouseClick);
102   glutMotionFunc(mouseDrag);
103   glutPassiveMotionFunc(mouseMove);
104 
105   delete mRI;
106   mRI = new gui::OpenGLRenderInterface();
107   mRI->initialize();
108   // glutTimerFunc(mDisplayTimeout, refreshTimer, 0);
109   // glutTimerFunc(mDisplayTimeout, runTimer, 0);
110 
111 #ifndef _WIN32
112   glDisable(GL_MULTISAMPLE);
113 #endif
114   // TODO: Disabled use of GL_MULTISAMPLE for Windows. Please see #411 for the
115   // detail.
116 
117   glutTimerFunc(mDisplayTimeout, refreshTimer, 0);
118   // Note: We book the timer id 0 for the main rendering purpose.
119 }
120 
reshape(int _w,int _h)121 void Window::reshape(int _w, int _h)
122 {
123   current()->mScreenshotTemp = std::vector<unsigned char>(_w * _h * 4);
124   current()->mScreenshotTemp2 = std::vector<unsigned char>(_w * _h * 4);
125   current()->resize(_w, _h);
126 }
127 
keyEvent(unsigned char _key,int _x,int _y)128 void Window::keyEvent(unsigned char _key, int _x, int _y)
129 {
130   current()->keyboard(_key, _x, _y);
131 }
132 
specKeyEvent(int _key,int _x,int _y)133 void Window::specKeyEvent(int _key, int _x, int _y)
134 {
135   current()->specKey(_key, _x, _y);
136 }
137 
mouseClick(int _button,int _state,int _x,int _y)138 void Window::mouseClick(int _button, int _state, int _x, int _y)
139 {
140   current()->click(_button, _state, _x, _y);
141 }
142 
mouseDrag(int _x,int _y)143 void Window::mouseDrag(int _x, int _y)
144 {
145   current()->drag(_x, _y);
146 }
147 
mouseMove(int _x,int _y)148 void Window::mouseMove(int _x, int _y)
149 {
150   current()->move(_x, _y);
151 }
152 
refresh()153 void Window::refresh()
154 {
155   current()->render();
156 }
157 
refreshTimer(int _val)158 void Window::refreshTimer(int _val)
159 {
160   current()->displayTimer(_val);
161 }
162 
displayTimer(int _val)163 void Window::displayTimer(int _val)
164 {
165   glutPostRedisplay();
166   glutTimerFunc(mDisplayTimeout, refreshTimer, _val);
167 }
168 
simTimer(int)169 void Window::simTimer(int /*_val*/)
170 {
171 }
172 
runTimer(int _val)173 void Window::runTimer(int _val)
174 {
175   current()->simTimer(_val);
176 }
177 
screenshot()178 bool Window::screenshot()
179 {
180   static int count = 0;
181   const char directory[8] = "frames";
182   const char fileBase[8] = "Capture";
183   char fileName[32];
184 
185   // create frames directory if not exists
186   using Stat = struct stat;
187   Stat buff;
188 
189 #ifdef _WIN32
190 #  define __S_ISTYPE(mode, mask) (((mode)&_S_IFMT) == (mask))
191 #  define S_ISDIR(mode) __S_ISTYPE((mode), _S_IFDIR)
192   if (stat(directory, &buff) != 0)
193     _mkdir(directory);
194 #else
195   if (stat(directory, &buff) != 0)
196     mkdir(directory, 0777);
197 #endif
198 
199   if (!S_ISDIR(buff.st_mode))
200   {
201     dtwarn << "[Window::screenshot] 'frames' is not a directory, "
202            << "cannot write a screenshot\n";
203     return false;
204   }
205 
206   // png
207 #ifdef _WIN32
208   _snprintf(
209       fileName,
210       sizeof(fileName),
211       "%s%s%s%.4d.png",
212       directory,
213       "\\",
214       fileBase,
215       count++);
216 #else
217   std::snprintf(
218       fileName,
219       sizeof(fileName),
220       "%s%s%s%.4d.png",
221       directory,
222       "/",
223       fileBase,
224       count++);
225 #endif
226   int tw = glutGet(GLUT_WINDOW_WIDTH);
227   int th = glutGet(GLUT_WINDOW_HEIGHT);
228 
229   glReadPixels(0, 0, tw, th, GL_RGBA, GL_UNSIGNED_BYTE, &mScreenshotTemp[0]);
230 
231   // reverse temp2 temp1
232   for (int row = 0; row < th; row++)
233   {
234     memcpy(
235         &mScreenshotTemp2[row * tw * 4],
236         &mScreenshotTemp[(th - row - 1) * tw * 4],
237         tw * 4);
238   }
239 
240   unsigned result = lodepng::encode(fileName, mScreenshotTemp2, tw, th);
241 
242   // if there's an error, display it
243   if (result)
244   {
245     std::cout << "lodepng error " << result << ": "
246               << lodepng_error_text(result) << std::endl;
247     return false;
248   }
249   else
250   {
251     std::cout << "wrote screenshot " << fileName << "\n";
252     return true;
253   }
254 }
255 
current()256 inline Window* Window::current()
257 {
258   int id = glutGetWindow();
259   for (unsigned int i = 0; i < mWinIDs.size(); i++)
260   {
261     if (mWinIDs.at(i) == id)
262     {
263       return mWindows.at(i);
264     }
265   }
266   std::cout << "An unknown error occurred!" << std::endl;
267   exit(0);
268 }
269 
keyboard(unsigned char,int,int)270 void Window::keyboard(unsigned char /*_key*/, int /*_x*/, int /*_y*/)
271 {
272   // TODO(JS): Is 2d point information necessary for keyboard event?
273 }
274 
specKey(int,int,int)275 void Window::specKey(int /*_key*/, int /*_x*/, int /*_y*/)
276 {
277 }
278 
click(int,int,int,int)279 void Window::click(int /*_button*/, int /*_state*/, int /*_x*/, int /*_y*/)
280 {
281 }
282 
drag(int,int)283 void Window::drag(int /*_x*/, int /*_y*/)
284 {
285 }
286 
move(int,int)287 void Window::move(int /*_x*/, int /*_y*/)
288 {
289 }
290 
291 } // namespace glut
292 } // namespace gui
293 } // namespace dart
294