moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
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>
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
55using namespace std;
56
57mesh_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
83void 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
94void 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
104void 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
112void 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
128void 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
170void 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
195{
196 begin();
197 glCallList(list);
198 end();
199}
200
202{
203 glFlush();
204 glPopAttrib();
205 glBindFramebuffer(GL_FRAMEBUFFER, 0);
206}
207
208void 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
224GLuint 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
237GLuint 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
258GLuint 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
288void 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
313GLuint 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
359map<std::thread::id, pair<unsigned, GLuint> > mesh_filter::GLRenderer::s_context;
360std::mutex mesh_filter::GLRenderer::s_context_lock;
361bool mesh_filter::GLRenderer::s_glut_initialized = false;
362
363namespace
364{
365void nullDisplayFunction()
366{
367}
368} // namespace
369
370void 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
417void 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
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
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.
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
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