MRPT  1.9.9
CRenderizableShaderTexturedTriangles.cpp
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | https://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2020, Individual contributors, see AUTHORS file |
6  | See: https://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See: https://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 
10 #include "opengl-precomp.h" // Precompiled header
11 
16 #include <iostream>
17 #include <memory> // std::align
18 #include <thread>
19 
20 #include <mrpt/opengl/opengl_api.h>
21 
22 using namespace mrpt;
23 using namespace mrpt::opengl;
24 using namespace mrpt::poses;
25 using namespace mrpt::math;
26 using namespace std;
27 using mrpt::img::CImage;
28 
31 
32 // Whether to profile memory allocations:
33 //#define TEXTUREOBJ_PROFILE_MEM_ALLOC
34 
35 // Whether to use a memory pool for the texture buffer:
36 #define TEXTUREOBJ_USE_MEMPOOL
37 
39 {
40 #if MRPT_HAS_OPENGL_GLUT
41  // Generate vertices & colors into m_triangles
42  const_cast<CRenderizableShaderTexturedTriangles&>(*this)
44 
45  const auto n = m_triangles.size();
46 
47  // Define OpenGL buffers:
48  m_vertexBuffer.createOnce();
49  m_vertexBuffer.bind();
50  m_vertexBuffer.allocate(m_triangles.data(), sizeof(m_triangles[0]) * n);
51 
52  // VAO: required to use glEnableVertexAttribArray()
53  m_vao.createOnce();
54 
55 #endif
56 }
57 
58 void CRenderizableShaderTexturedTriangles::render(const RenderContext& rc) const
59 {
60 #if MRPT_HAS_OPENGL_GLUT
61 
62  // This will load and/or select our texture, only once:
63  initializeTextures();
64 
65  // Set the texture uniform:
66  {
67  const Program& s = *rc.shader;
68  // bound to GL_TEXTURE0:
69  glUniform1i(s.uniformId("textureSampler"), 0);
70  }
71 
72  // Enable/disable lights:
73  {
74  const Program& s = *rc.shader;
75  GLint enabled = m_enableLight ? 1 : 0;
76  glUniform1i(s.uniformId("enableLight"), enabled);
77  CHECK_OPENGL_ERROR();
78  }
79 
80  if (m_enableLight && rc.lights && rc.shader->hasUniform("light_diffuse"))
81  {
82  const Program& s = *rc.shader;
83  glUniform4fv(s.uniformId("light_diffuse"), 1, &rc.lights->diffuse.R);
84  glUniform4fv(s.uniformId("light_ambient"), 1, &rc.lights->ambient.R);
85  glUniform4fv(s.uniformId("light_specular"), 1, &rc.lights->specular.R);
86  glUniform3fv(
87  s.uniformId("light_direction"), 1, &rc.lights->direction.x);
88  CHECK_OPENGL_ERROR();
89  }
90 
91  // Set up the vertex array:
92  const GLuint attr_position = rc.shader->attributeId("position");
93  m_vao.bind();
94  glEnableVertexAttribArray(attr_position);
95  m_vertexBuffer.bind();
96  glVertexAttribPointer(
97  attr_position, /* attribute */
98  3, /* size */
99  GL_FLOAT, /* type */
100  GL_FALSE, /* normalized? */
101  sizeof(TTriangle::Vertex), /* stride */
102  BUFFER_OFFSET(offsetof(TTriangle::Vertex, xyzrgba.pt.x)));
103  CHECK_OPENGL_ERROR();
104 
105  // Set up the normals array:
106  const GLuint attr_normals = rc.shader->attributeId("vertexNormal");
107  glEnableVertexAttribArray(attr_normals);
108  m_vertexBuffer.bind();
109  glVertexAttribPointer(
110  attr_normals, /* attribute */
111  3, /* size */
112  GL_FLOAT, /* type */
113  GL_FALSE, /* normalized? */
114  sizeof(TTriangle::Vertex), /* stride */
115  BUFFER_OFFSET(offsetof(TTriangle::Vertex, normal.x)));
116  CHECK_OPENGL_ERROR();
117 
118  // Set up the UV array:
119  const GLuint attr_uv = rc.shader->attributeId("vertexUV");
120  glEnableVertexAttribArray(attr_uv);
121  m_vertexBuffer.bind();
122  glVertexAttribPointer(
123  attr_uv, /* attribute */
124  2, /* size */
125  GL_FLOAT, /* type */
126  GL_FALSE, /* normalized? */
127  sizeof(TTriangle::Vertex), /* stride */
128  BUFFER_OFFSET(offsetof(TTriangle::Vertex, uv.x)));
129  CHECK_OPENGL_ERROR();
130 
131  // Draw:
132  glDrawArrays(GL_TRIANGLES, 0, 3 * m_triangles.size());
133  CHECK_OPENGL_ERROR();
134 
135  glDisableVertexAttribArray(attr_position);
136  glDisableVertexAttribArray(attr_uv);
137  glDisableVertexAttribArray(attr_normals);
138 
139 #endif
140 }
141 
142 // Data types for memory pooling CRenderizableShaderTexturedTriangles:
143 #ifdef TEXTUREOBJ_USE_MEMPOOL
144 
146 
147 struct CRenderizableShaderTexturedTriangles_MemPoolParams
148 {
149  /** size of the vector<unsigned char> */
150  size_t len = 0;
151 
152  inline bool isSuitable(
153  const CRenderizableShaderTexturedTriangles_MemPoolParams& req) const
154  {
155  return len == req.len;
156  }
157 };
159 {
160  vector<unsigned char> data;
161 };
162 
164  CRenderizableShaderTexturedTriangles_MemPoolParams,
166 #endif
167 
169  const CImage& img, const CImage& imgAlpha)
170 {
171  MRPT_START
172 
174 
175  unloadTexture();
176 
177  // Make a copy:
178  m_textureImage = img;
179  m_textureImageAlpha = imgAlpha;
180 
181  m_enableTransparency = true;
182 
183  MRPT_END
184 }
185 
187 {
188  MRPT_START
189 
191 
192  unloadTexture();
193 
194  // Make a copy:
195  m_textureImage = img;
196 
197  m_enableTransparency = false;
198 
199  MRPT_END
200 }
201 
203  CImage&& img, CImage&& imgAlpha)
204 {
205  MRPT_START
206 
208 
209  unloadTexture();
210 
211  m_textureImage = std::move(img);
212  m_textureImageAlpha = std::move(imgAlpha);
213 
214  m_enableTransparency = true;
215 
216  MRPT_END
217 }
218 
220 {
221  MRPT_START
222 
224 
225  unloadTexture();
226 
227  m_textureImage = std::move(img);
228 
229  m_enableTransparency = false;
230 
231  MRPT_END
232 }
233 
234 // Auxiliary function for loadTextureInOpenGL(): reserve memory and return
235 // 16byte aligned starting point within it:
236 static unsigned char* reserveDataBuffer(
237  const size_t len, vector<unsigned char>& data)
238 {
239 #ifdef TEXTUREOBJ_USE_MEMPOOL
241  if (pool)
242  {
243  CRenderizableShaderTexturedTriangles_MemPoolParams mem_params;
244  mem_params.len = len;
245 
247  pool->request_memory(mem_params);
248  if (mem_block)
249  {
250  // Recover the memory block via a swap:
251  data.swap(mem_block->data);
252  delete mem_block;
253  }
254  }
255 #endif
256  data.resize(len);
257  void* ptr = &data[0];
258  size_t space = len;
259  return reinterpret_cast<unsigned char*>(
260  std::align(16, 1 /*dummy size*/, ptr, space));
261 }
262 
264 {
265 #if MRPT_HAS_OPENGL_GLUT
266  unsigned char* dataAligned = nullptr;
267  vector<unsigned char> data;
268 
269 #ifdef TEXTUREOBJ_PROFILE_MEM_ALLOC
270  static mrpt::system::CTimeLogger tim;
271 #endif
272 
273  // Do nothing until we are assigned an image.
274  if (m_textureImage.isEmpty()) return;
275 
276  try
277  {
278  if (m_texture_is_loaded)
279  {
280  // activate the texture unit first before binding texture
281  glActiveTexture(GL_TEXTURE0);
282  glBindTexture(GL_TEXTURE_2D, m_glTextureName);
283  CHECK_OPENGL_ERROR();
284  return;
285  }
286 
287  // Reserve the new one --------------------------
288  ASSERT_(m_textureImage.getPixelDepth() == mrpt::img::PixelDepth::D8U);
289 
290  // allocate texture names:
291  m_glTextureName = getNewTextureNumber();
292 
293  // activate the texture unit first before binding texture
294  glActiveTexture(GL_TEXTURE0);
295  // select our current texture
296  glBindTexture(GL_TEXTURE_2D, m_glTextureName);
297  CHECK_OPENGL_ERROR();
298 
299  // when texture area is small, linear interpolation. Default is
300  // GL_LINEAR_MIPMAP_NEAREST but we
301  // are not building mipmaps.
302  // See also:
303  // http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=133116&page=1
304  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
305  CHECK_OPENGL_ERROR();
306 
307  // when texture area is large, NEAREST: this is mainly thinking of
308  // rendering
309  // occupancy grid maps, such as we want those "big pixels" to be
310  // clearly visible ;-)
311  glTexParameterf(
312  GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
313  m_textureInterpolate ? GL_LINEAR : GL_NEAREST);
314  CHECK_OPENGL_ERROR();
315 
316  // if wrap is true, the texture wraps over at the edges (repeat)
317  // ... false, the texture ends at the edges (clamp)
318  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
319  CHECK_OPENGL_ERROR();
320 
321  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
322  CHECK_OPENGL_ERROR();
323 
324  // Assure that the images do not overpass the maximum dimensions allowed
325  // by OpenGL:
326  // ------------------------------------------------------------------------------------
327  GLint texSize;
328  glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize);
329  while (m_textureImage.getHeight() > (unsigned int)texSize ||
330  m_textureImage.getWidth() > (unsigned int)texSize)
331  {
332  m_textureImage =
333  m_textureImage.scaleHalf(mrpt::img::IMG_INTERP_LINEAR);
334  m_textureImageAlpha =
335  m_textureImageAlpha.scaleHalf(mrpt::img::IMG_INTERP_LINEAR);
336  }
337 
338  const int width = m_textureImage.getWidth();
339  const int height = m_textureImage.getHeight();
340 
341 #ifdef TEXTUREOBJ_PROFILE_MEM_ALLOC
342  {
343  const std::string sSec = mrpt::format(
344  "opengl_texture: load %ix%i %s %stransp", width, height,
345  m_textureImage.isColor() ? "RGB" : "BW",
346  m_enableTransparency ? "" : "no ");
347  tim.enter(sSec.c_str());
348  }
349 #endif
350 
351  if (m_enableTransparency)
352  {
353  ASSERT_(!m_textureImageAlpha.isColor());
355  m_textureImageAlpha.getWidth(), m_textureImage.getWidth());
357  m_textureImageAlpha.getHeight(), m_textureImage.getHeight());
358  }
359 
360  // GL_LUMINANCE and GL_LUMINANCE_ALPHA were removed in OpenGL 3.1
361  // Convert grayscale images into color:
362  if (!m_textureImage.isColor())
363  m_textureImage = m_textureImage.colorImage();
364 
365  // Color texture:
366  if (m_enableTransparency)
367  {
368 // Color texture WITH trans.
369 // --------------------------------------
370 #ifdef TEXTUREOBJ_PROFILE_MEM_ALLOC
371  const std::string sSec = mrpt::format(
372  "opengl_texture_alloc %ix%i (color,trans)", width, height);
373  tim.enter(sSec.c_str());
374 #endif
375 
376  dataAligned = reserveDataBuffer(height * width * 4 + 512, data);
377 
378 #ifdef TEXTUREOBJ_PROFILE_MEM_ALLOC
379  tim.leave(sSec.c_str());
380 #endif
381 
382  for (int y = 0; y < height; y++)
383  {
384  unsigned char* ptrSrcCol = m_textureImage(0, y, 0);
385  unsigned char* ptrSrcAlfa = m_textureImageAlpha(0, y);
386  unsigned char* ptr = dataAligned + y * width * 4;
387 
388  for (int x = 0; x < width; x++)
389  {
390  *ptr++ = *ptrSrcCol++;
391  *ptr++ = *ptrSrcCol++;
392  *ptr++ = *ptrSrcCol++;
393  *ptr++ = *ptrSrcAlfa++;
394  }
395  }
396 
397  // Prepare image data types:
398  const GLenum img_type = GL_UNSIGNED_BYTE;
399  // Reverse RGB <-> BGR order?
400  const bool is_RGB_order =
401  (m_textureImage.getChannelsOrder() == std::string("RGB"));
402  const GLenum img_format = (is_RGB_order ? GL_RGBA : GL_BGRA);
403 
404  // Send image data to OpenGL:
405  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
406  glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
407  glTexImage2D(
408  GL_TEXTURE_2D, 0 /*level*/, GL_RGBA8 /* RGB components */,
409  width, height, 0 /*border*/, img_format, img_type, dataAligned);
410  CHECK_OPENGL_ERROR();
411  glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); // Reset
412  CHECK_OPENGL_ERROR();
413 
414  } // End of color texture WITH trans.
415  else
416  {
417  // Color texture WITHOUT trans.
418  // --------------------------------------
419  // Prepare image data types:
420  const GLenum img_type = GL_UNSIGNED_BYTE;
421  const int nBytesPerPixel = m_textureImage.isColor() ? 3 : 1;
422  // Reverse RGB <-> BGR order?
423  const bool is_RGB_order =
424  (m_textureImage.getChannelsOrder() == std::string("RGB"));
425  const GLenum img_format = nBytesPerPixel == 3
426  ? (is_RGB_order ? GL_RGB : GL_BGR)
427  : GL_LUMINANCE;
428 
429  // Send image data to OpenGL:
430  glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
431  CHECK_OPENGL_ERROR();
432  glPixelStorei(
433  GL_UNPACK_ROW_LENGTH,
434  m_textureImage.getRowStride() / nBytesPerPixel);
435  CHECK_OPENGL_ERROR();
436  glTexImage2D(
437  GL_TEXTURE_2D, 0 /*level*/, GL_RGB8 /* RGB components */, width,
438  height, 0 /*border*/, img_format, img_type,
439  m_textureImage.ptrLine<uint8_t>(0));
440  CHECK_OPENGL_ERROR();
441  glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); // Reset
442  CHECK_OPENGL_ERROR();
443 
444  } // End of color texture WITHOUT trans.
445 
446  m_texture_is_loaded = true;
447 
448 #ifdef TEXTUREOBJ_PROFILE_MEM_ALLOC
449  {
450  const std::string sSec = mrpt::format(
451  "opengl_texture: load %ix%i %s %stransp", width, height,
452  m_textureImage.isColor() ? "RGB" : "BW",
453  m_enableTransparency ? "" : "no ");
454  tim.leave(sSec.c_str());
455  }
456 #endif
457 
458 #ifdef TEXTUREOBJ_USE_MEMPOOL
459  // Before freeing the buffer in "data", donate my memory to the pool:
460  if (!data.empty())
461  {
463  if (pool)
464  {
465  CRenderizableShaderTexturedTriangles_MemPoolParams mem_params;
466  mem_params.len = data.size();
467 
468  auto* mem_block =
470  data.swap(mem_block->data);
471 
472  pool->dump_to_pool(mem_params, mem_block);
473  }
474  }
475 #endif
476  }
477  catch (exception& e)
478  {
480  format("m_glTextureName=%i\n%s", m_glTextureName, e.what()));
481  }
482  catch (...)
483  {
484  THROW_EXCEPTION("Runtime error!");
485  }
486 #endif
487 }
488 
490 {
491  try
492  {
493  unloadTexture();
494  }
495  catch (const std::exception& e)
496  {
497  std::cerr
498  << "[~CRenderizableShaderTexturedTriangles] Ignoring exception: "
500  }
501 }
503 {
504  if (m_texture_is_loaded)
505  {
506  m_texture_is_loaded = false;
507  releaseTextureName(m_glTextureName);
508  m_glTextureName = 0;
509  }
510 }
511 
514 {
515  uint8_t ver = 0;
516 
517  out << ver;
518  out << m_enableTransparency << m_textureInterpolate;
519  out << m_textureImage;
520  if (m_enableTransparency) out << m_textureImageAlpha;
521 }
522 
525 {
526  uint8_t version;
527  in >> version;
528 
530 
531  switch (version)
532  {
533  case 0:
534  {
535  in >> m_enableTransparency >> m_textureInterpolate;
536  in >> m_textureImage;
537  if (m_enableTransparency)
538  {
539  in >> m_textureImageAlpha;
540  assignImage(m_textureImage, m_textureImageAlpha);
541  }
542  else
543  {
544  assignImage(m_textureImage);
545  }
546  }
547  break;
548  default:
550  };
552 }
553 
554 static std::map<unsigned int, std::thread::id> textureReservedFrom;
555 
557 {
558  MRPT_START
559 #if MRPT_HAS_OPENGL_GLUT
560  // Create one OpenGL texture
561  GLuint textureID;
562  glGenTextures(1, &textureID);
563 
564  textureReservedFrom[textureID] = std::this_thread::get_id();
565  return textureID;
566 #else
567  THROW_EXCEPTION("This function needs OpenGL");
568 #endif
569  MRPT_END
570 }
571 
573 {
574  MRPT_START
575 #if MRPT_HAS_OPENGL_GLUT
576 
577  auto it = textureReservedFrom.find(i);
578  if (it == textureReservedFrom.end()) return;
579 
580  if (std::this_thread::get_id() == it->second)
581  {
582  GLuint t = i;
583  glDeleteTextures(1, &t);
584  CHECK_OPENGL_ERROR();
585  }
586 
587  textureReservedFrom.erase(it);
588 #endif
589  MRPT_END
590 }
#define IMPLEMENTS_VIRTUAL_SERIALIZABLE(class_name, base_class, NS)
This must be inserted as implementation of some required members for virtual CSerializable classes: ...
void renderUpdateBuffers() const override
Called whenever m_outdatedBuffers is true: used to re-generate OpenGL vertex buffers, etc.
#define MRPT_START
Definition: exceptions.h:241
#define THROW_EXCEPTION(msg)
Definition: exceptions.h:67
std::string std::string format(std::string_view fmt, ARGS &&... args)
Definition: format.h:26
void notifyChange() const
Call to enable calling renderUpdateBuffers() before the next render() rendering iteration.
void dump_to_pool(const DATA_PARAMS &params, POOLABLE_DATA *block)
Saves the passed data block (characterized by params) to the pool.
The base class of 3D objects that can be directly rendered through OpenGL.
Definition: CRenderizable.h:48
STL namespace.
POOLABLE_DATA * request_memory(const DATA_PARAMS &params)
Request a block of data which fulfils the size requirements stated in params.
#define MRPT_THROW_UNKNOWN_SERIALIZATION_VERSION(__V)
For use in CSerializable implementations.
Definition: exceptions.h:97
A generic system for versatile memory pooling.
#define ASSERT_(f)
Defines an assertion mechanism.
Definition: exceptions.h:120
mrpt::img::CImage CImage
Definition: utils/CImage.h:5
static unsigned char * reserveDataBuffer(const size_t len, vector< unsigned char > &data)
This base provides a set of functions for maths stuff.
#define ASSERT_EQUAL_(__A, __B)
Assert comparing two values, reporting their actual values upon failure.
Definition: exceptions.h:137
void render(const RenderContext &rc) const override
Implements the rendering of 3D objects in each class derived from CRenderizable.
virtual void onUpdateBuffers_TexturedTriangles()=0
Must be implemented in derived classes to update the geometric entities to be drawn in "m_*_buffer" f...
Classes for 2D/3D geometry representation, both of single values and probability density distribution...
void enter(const std::string_view &func_name) noexcept
Start of a named section.
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
Virtual base class for "archives": classes abstracting I/O streams.
Definition: CArchive.h:54
static std::map< unsigned int, std::thread::id > textureReservedFrom
A versatile "profiler" that logs the time spent within each pair of calls to enter(X)-leave(X), among other stats.
mrpt::vision::TStereoCalibResults out
#define MRPT_END
Definition: exceptions.h:245
void writeToStreamTexturedObject(mrpt::serialization::CArchive &out) const
The namespace for 3D scene representation and rendering.
Definition: CGlCanvasBase.h:13
double leave(const std::string_view &func_name) noexcept
End of a named section.
std::string exception_to_str(const std::exception &e)
Builds a nice textual representation of a nested exception, which if generated using MRPT macros (THR...
Definition: exceptions.cpp:59
int uniformId(const char *name) const
Definition: Shader.h:113
A resource handling helper for OpenGL Shader "programs".
Definition: Shader.h:77
virtual void initializeTextures() const override
VERY IMPORTANT: If you use a multi-thread application, you MUST call this from the same thread that w...
Renderizable generic renderer for objects using the triangles-with-a-texture shader.
void assignImage(const mrpt::img::CImage &img, const mrpt::img::CImage &imgAlpha)
Assigns a texture and a transparency image, and enables transparency (If the images are not 2^N x 2^M...
A class for storing images as grayscale or RGB bitmaps.
Definition: img/CImage.h:148
#define BUFFER_OFFSET(offset)
static CGenericMemoryPool< DATA_PARAMS, POOLABLE_DATA > * getInstance(const size_t max_pool_entries=5)
Construct-on-first-use (~singleton) pattern: Return the unique instance of this class for a given tem...
static struct FontData data
Definition: gltext.cpp:144



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: c7a3bec24 Sun Mar 29 18:33:13 2020 +0200 at dom mar 29 18:50:38 CEST 2020