moveit2
The MoveIt Motion Planning Framework for ROS 2.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
gl_renderer.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2013, Willow Garage, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of Willow Garage nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *********************************************************************/
34 
35 /* Author: Suat Gedikli */
36 
37 #include <GL/glew.h>
38 #ifdef __APPLE__
39 #include <OpenGL/glu.h>
40 #else
41 #include <GL/glu.h>
42 #include <GL/glut.h>
43 #endif
44 #include <GL/freeglut.h>
46 #include <moveit/utils/logger.hpp>
47 #include <sstream>
48 #include <fstream>
49 #include <stdexcept>
50 #include <vector>
51 #include <thread>
52 #include <rclcpp/logger.hpp>
53 #include <rclcpp/logging.hpp>
54 
55 using namespace std;
56 
57 mesh_filter::GLRenderer::GLRenderer(unsigned width, unsigned height, double near, double far)
58  : width_(width)
59  , height_(height)
60  , fbo_id_(0)
61  , rbo_id_(0)
62  , rgb_id_(0)
63  , depth_id_(0)
64  , program_(0)
65  , near_(near)
66  , far_(far)
67  , fx_(width >> 1) // 90 degree wide angle
68  , fy_(fx_)
69  , cx_(width >> 1)
70  , cy_(height >> 1)
71 {
72  createGLContext();
73  initFrameBuffers();
74 }
75 
77 {
78  glDeleteProgram(program_);
79  deleteFrameBuffers();
80  deleteGLContext();
81 }
82 
83 void mesh_filter::GLRenderer::setBufferSize(unsigned width, unsigned height)
84 {
85  if (width_ != width || height_ != height)
86  {
87  width_ = width;
88  height_ = height;
89  deleteFrameBuffers();
90  initFrameBuffers();
91  }
92 }
93 
94 void mesh_filter::GLRenderer::setClippingRange(double near, double far)
95 {
96  if (near_ <= 0)
97  throw runtime_error("near clipping plane distance needs to be larger than 0");
98  if (far_ <= near_)
99  throw runtime_error("far clipping plane needs to be larger than near clipping plane distance");
100  near_ = near;
101  far_ = far;
102 }
103 
104 void mesh_filter::GLRenderer::setCameraParameters(double fx, double fy, double cx, double cy)
105 {
106  fx_ = fx;
107  fy_ = fy;
108  cx_ = cx;
109  cy_ = cy;
110 }
111 
112 void mesh_filter::GLRenderer::setCameraParameters() const
113 {
114  double left = near_ * -cx_ / fx_;
115  double right = near_ * (width_ - cx_) / fx_;
116  double top = near_ * cy_ / fy_;
117  double bottom = near_ * (cy_ - height_) / fy_;
118 
119  glMatrixMode(GL_PROJECTION);
120  glLoadIdentity();
121  glFrustum(left, right, bottom, top, near_, far_);
122 
123  glMatrixMode(GL_MODELVIEW);
124  glLoadIdentity();
125  gluLookAt(0, 0, 0, 0, 0, 1, 0, -1, 0);
126 }
127 
128 void mesh_filter::GLRenderer::initFrameBuffers()
129 {
130  glGenTextures(1, &rgb_id_);
131  glBindTexture(GL_TEXTURE_2D, rgb_id_);
132  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width_, height_, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
133  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
134  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
135  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
136  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
137  glBindTexture(GL_TEXTURE_2D, 0);
138 
139  glGenTextures(1, &depth_id_);
140  glBindTexture(GL_TEXTURE_2D, depth_id_);
141  glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width_, height_, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr);
142  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
143  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
144  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
145  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
146  glBindTexture(GL_TEXTURE_2D, 0);
147 
148  glGenFramebuffers(1, &fbo_id_);
149  glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
150  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rgb_id_, 0);
151 
152  glGenRenderbuffers(1, &rbo_id_);
153  glBindRenderbuffer(GL_RENDERBUFFER, rbo_id_);
154  glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width_, height_);
155  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo_id_);
156  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_id_, 0);
157  glBindRenderbuffer(GL_RENDERBUFFER, 0);
158 
159  GLenum draw_buffers[2] = { GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT };
160  glDrawBuffers(2, draw_buffers);
161 
162  GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
163 
164  if (status != GL_FRAMEBUFFER_COMPLETE) // If the frame buffer does not report back as complete
165  throw runtime_error("Couldn't create frame buffer");
166 
167  glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind our frame buffer
168 }
169 
170 void mesh_filter::GLRenderer::deleteFrameBuffers()
171 {
172  if (rbo_id_)
173  glDeleteRenderbuffers(1, &rbo_id_);
174  if (fbo_id_)
175  glDeleteFramebuffers(1, &fbo_id_);
176  if (depth_id_)
177  glDeleteTextures(1, &depth_id_);
178  if (rgb_id_)
179  glDeleteTextures(1, &rgb_id_);
180 
181  rbo_id_ = fbo_id_ = depth_id_ = rgb_id_ = 0;
182 }
183 
185 {
186  glPushAttrib(GL_VIEWPORT_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_POLYGON_BIT | GL_PIXEL_MODE_BIT);
187  glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
188  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
189  glViewport(0, 0, width_, height_);
190  glUseProgram(program_);
191  setCameraParameters();
192 }
193 
194 void mesh_filter::GLRenderer::callList(GLuint list) const
195 {
196  begin();
197  glCallList(list);
198  end();
199 }
200 
202 {
203  glFlush();
204  glPopAttrib();
205  glBindFramebuffer(GL_FRAMEBUFFER, 0);
206 }
207 
208 void mesh_filter::GLRenderer::getColorBuffer(unsigned char* buffer) const
209 {
210  glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
211  glBindTexture(GL_TEXTURE_2D, rgb_id_);
212  glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
213  glBindFramebuffer(GL_FRAMEBUFFER, 0);
214 }
215 
217 {
218  glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
219  glBindTexture(GL_TEXTURE_2D, depth_id_);
220  glGetTexImage(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, GL_FLOAT, buffer);
221  glBindFramebuffer(GL_FRAMEBUFFER, 0);
222 }
223 
224 GLuint mesh_filter::GLRenderer::setShadersFromFile(const string& vertex_filename, const string& fragment_filename)
225 {
226  if (program_)
227  glDeleteProgram(program_);
228 
229  string vertex_source, fragment_source;
230  readShaderCodeFromFile(vertex_filename, vertex_source);
231  readShaderCodeFromFile(fragment_filename, fragment_source);
232 
233  program_ = loadShaders(vertex_source, fragment_source);
234  return program_;
235 }
236 
237 GLuint mesh_filter::GLRenderer::setShadersFromString(const string& vertex_source, const string& fragment_source)
238 {
239  program_ = loadShaders(vertex_source, fragment_source);
240  return program_;
241 }
242 
244 {
245  return program_;
246 }
247 
249 {
250  return near_;
251 }
252 
254 {
255  return far_;
256 }
257 
258 GLuint mesh_filter::GLRenderer::createShader(GLuint shaderType, const string& ShaderCode) const
259 {
260  GLuint shader_id = glCreateShader(shaderType);
261 
262  // Compile Shader
263  const char* source_pointer = ShaderCode.c_str();
264  glShaderSource(shader_id, 1, &source_pointer, nullptr);
265  glCompileShader(shader_id);
266 
267  // Check Shader
268  GLint result = GL_FALSE;
269  glGetShaderiv(shader_id, GL_COMPILE_STATUS, &result);
270  if (result != GL_TRUE)
271  {
272  int info_log_length;
273  glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &info_log_length);
274  if (info_log_length > 0)
275  {
276  vector<char> shader_error_message(info_log_length + 1);
277  glGetShaderInfoLog(shader_id, info_log_length, nullptr, &shader_error_message[0]);
278  stringstream error_stream;
279  error_stream << "Could not compile shader: " << const_cast<const char*>(&shader_error_message[0]);
280 
281  glDeleteShader(shader_id);
282  throw runtime_error(error_stream.str());
283  }
284  }
285  return shader_id;
286 }
287 
288 void mesh_filter::GLRenderer::readShaderCodeFromFile(const string& filename, string& shader) const
289 {
290  if (filename.empty())
291  {
292  shader = "";
293  }
294  else
295  {
296  string shader_code;
297  fstream shader_file(filename.c_str(), ios::in);
298  if (shader_file.is_open())
299  {
300  stringstream buffer;
301  buffer << shader_file.rdbuf();
302  shader = buffer.str();
303  }
304  else
305  {
306  stringstream error_stream;
307  error_stream << "Could not open shader code in file \"" << filename << '\"';
308  throw runtime_error(error_stream.str());
309  }
310  }
311 }
312 
313 GLuint mesh_filter::GLRenderer::loadShaders(const string& vertex_source, const string& fragment_source) const
314 {
315  if (vertex_source.empty() && fragment_source.empty())
316  return 0;
317 
318  GLuint program_id = glCreateProgram();
319  GLuint vertex_shader_id = 0;
320  GLuint fragment_shader_id = 0;
321 
322  if (!vertex_source.empty())
323  {
324  GLuint vertex_shader_id = createShader(GL_VERTEX_SHADER, vertex_source);
325  glAttachShader(program_id, vertex_shader_id);
326  }
327 
328  if (!fragment_source.empty())
329  {
330  GLuint fragment_shader_id = createShader(GL_FRAGMENT_SHADER, fragment_source);
331  glAttachShader(program_id, fragment_shader_id);
332  }
333 
334  glLinkProgram(program_id);
335 
336  // Check the program
337  GLint result = GL_FALSE;
338  GLint info_log_length;
339  glGetProgramiv(program_id, GL_LINK_STATUS, &result);
340  glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &info_log_length);
341  if (info_log_length > 0)
342  {
343  vector<char> program_error_message(info_log_length + 1);
344  glGetProgramInfoLog(program_id, info_log_length, nullptr, &program_error_message[0]);
345  std::size_t l = strnlen(&program_error_message[0], program_error_message.size());
346  if (l > 0)
347  RCLCPP_ERROR(moveit::getLogger("moveit.ros.gl_renderer"), "%s\n", &program_error_message[0]);
348  }
349 
350  if (vertex_shader_id)
351  glDeleteShader(vertex_shader_id);
352 
353  if (fragment_shader_id)
354  glDeleteShader(fragment_shader_id);
355 
356  return program_id;
357 }
358 
359 map<std::thread::id, pair<unsigned, GLuint> > mesh_filter::GLRenderer::s_context;
360 std::mutex mesh_filter::GLRenderer::s_context_lock;
361 bool mesh_filter::GLRenderer::s_glut_initialized = false;
362 
363 namespace
364 {
365 void nullDisplayFunction()
366 {
367 }
368 } // namespace
369 
370 void mesh_filter::GLRenderer::createGLContext()
371 {
372  std::unique_lock<std::mutex> _(s_context_lock);
373  if (!s_glut_initialized)
374  {
375  char buffer[1];
376  char* args = buffer;
377  int n = 1;
378 
379  glutInit(&n, &args);
380  glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
381  s_glut_initialized = true;
382  }
383 
384  // check if our thread is initialized
385  std::thread::id thread_id = std::this_thread::get_id();
386  map<std::thread::id, pair<unsigned, GLuint> >::iterator context_it = s_context.find(thread_id);
387 
388  if (context_it == s_context.end())
389  {
390  s_context.insert({ thread_id, std::pair<unsigned, GLuint>(1, 0) });
391 
392  glutInitWindowPosition(glutGet(GLUT_SCREEN_WIDTH) + 30000, 0);
393  glutInitWindowSize(1, 1);
394  GLuint window_id = glutCreateWindow("mesh_filter");
395  glutDisplayFunc(nullDisplayFunction);
396 
397  GLenum err = glewInit();
398  if (GLEW_OK != err)
399  {
400  stringstream error_stream;
401  error_stream << "Unable to initialize GLEW: " << glewGetErrorString(err);
402 
403  throw(runtime_error(error_stream.str()));
404  }
405  glutIconifyWindow();
406  glutHideWindow();
407 
408  for (int i = 0; i < 10; ++i)
409  glutMainLoopEvent();
410 
411  s_context.at(thread_id) = std::pair<unsigned, GLuint>(1, window_id);
412  }
413  else
414  ++(context_it->second.first);
415 }
416 
417 void mesh_filter::GLRenderer::deleteGLContext()
418 {
419  std::unique_lock<std::mutex> _(s_context_lock);
420  std::thread::id thread_id = std::this_thread::get_id();
421  map<std::thread::id, pair<unsigned, GLuint> >::iterator context_it = s_context.find(thread_id);
422  if (context_it == s_context.end())
423  {
424  stringstream error_msg;
425  error_msg << "No OpenGL context exists for Thread " << thread_id;
426  throw runtime_error(error_msg.str());
427  }
428 
429  if (--(context_it->second.first) == 0)
430  {
431  glutDestroyWindow(context_it->second.second);
432  s_context.erase(context_it);
433  }
434 }
435 
437 {
438  return rgb_id_;
439 }
440 
442 {
443  return depth_id_;
444 }
445 
447 {
448  return width_;
449 }
450 
452 {
453  return height_;
454 }
const double & getFarClippingDistance() const
returns the distance of the far clipping plane in meters
void getDepthBuffer(float *buffer) const
retrieves the depth buffer from OpenGL
void setBufferSize(unsigned width, unsigned height)
set the size of frame buffers
Definition: gl_renderer.cpp:83
void callList(GLuint list) const
executes a OpenGL list
GLuint getColorTexture() const
returns the handle of the color buffer as an OpenGL texture object
GLuint getDepthTexture() const
returns the handle of the depth buffer as an OpenGL texture object
unsigned getWidth() const
returns the width of the frame buffer objectsin pixels
void end() const
finalizes the frame buffers after rendering and/or manipulating
void begin() const
initializes the frame buffers for rendering and or manipulating
GLuint setShadersFromFile(const std::string &vertex_filename, const std::string &fragment_filename)
loads, compiles, links and adds GLSL shaders from files to the current OpenGL context.
void setClippingRange(double near, double far)
sets the near and far clipping plane distances in meters
Definition: gl_renderer.cpp:94
void getColorBuffer(unsigned char *buffer) const
retrieves the color buffer from OpenGL
unsigned getHeight() const
returns the height of the frame buffer objects in pixels
GLRenderer(unsigned width, unsigned height, double near=0.1, double far=10.0)
constructs the frame buffer object in a new OpenGL context.
Definition: gl_renderer.cpp:57
const double & getNearClippingDistance() const
returns the distance of the near clipping plane in meters
GLuint setShadersFromString(const std::string &vertex_shader, const std::string &fragment_shader)
loads, compiles, links and adds GLSL shaders from string to the current OpenGL context.
~GLRenderer()
destructor, destroys frame buffer objects and OpenGL context
Definition: gl_renderer.cpp:76
void setCameraParameters(double fx, double fy, double cx, double cy)
set the camera parameters
const GLuint & getProgramID() const
rclcpp::Logger getLogger(const std::string &name)
Creates a namespaced logger.
Definition: logger.cpp:79