MRPT  2.0.2
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  m_textureImageAssigned = true;
181 
182  m_enableTransparency = true;
183 
184  MRPT_END
185 }
186 
188 {
189  MRPT_START
190 
192 
193  unloadTexture();
194 
195  // Make a copy:
196  m_textureImage = img;
197  m_textureImageAssigned = true;
198 
199  m_enableTransparency = false;
200 
201  MRPT_END
202 }
203 
205  CImage&& img, CImage&& imgAlpha)
206 {
207  MRPT_START
208 
210 
211  unloadTexture();
212 
213  m_textureImage = std::move(img);
214  m_textureImageAlpha = std::move(imgAlpha);
215  m_textureImageAssigned = true;
216 
217  m_enableTransparency = true;
218 
219  MRPT_END
220 }
221 
223 {
224  MRPT_START
225 
227 
228  unloadTexture();
229 
230  m_textureImage = std::move(img);
231  m_textureImageAssigned = true;
232 
233  m_enableTransparency = false;
234 
235  MRPT_END
236 }
237 
238 // Auxiliary function for loadTextureInOpenGL(): reserve memory and return
239 // 16byte aligned starting point within it:
240 static unsigned char* reserveDataBuffer(
241  const size_t len, vector<unsigned char>& data)
242 {
243 #ifdef TEXTUREOBJ_USE_MEMPOOL
245  if (pool)
246  {
247  CRenderizableShaderTexturedTriangles_MemPoolParams mem_params;
248  mem_params.len = len;
249 
251  pool->request_memory(mem_params);
252  if (mem_block)
253  {
254  // Recover the memory block via a swap:
255  data.swap(mem_block->data);
256  delete mem_block;
257  }
258  }
259 #endif
260  data.resize(len);
261  void* ptr = &data[0];
262  size_t space = len;
263  return reinterpret_cast<unsigned char*>(
264  std::align(16, 1 /*dummy size*/, ptr, space));
265 }
266 
268 {
269 #if MRPT_HAS_OPENGL_GLUT
270  unsigned char* dataAligned = nullptr;
271  vector<unsigned char> data;
272 
273 #ifdef TEXTUREOBJ_PROFILE_MEM_ALLOC
274  static mrpt::system::CTimeLogger tim;
275 #endif
276 
277  // Do nothing until we are assigned an image.
278  if (m_textureImage.isEmpty()) return;
279 
280  try
281  {
282  if (m_texture_is_loaded)
283  {
284  // activate the texture unit first before binding texture
285  glActiveTexture(GL_TEXTURE0);
286  glBindTexture(GL_TEXTURE_2D, m_glTextureName);
287  CHECK_OPENGL_ERROR();
288  return;
289  }
290 
291  // Reserve the new one --------------------------
292  ASSERT_(m_textureImage.getPixelDepth() == mrpt::img::PixelDepth::D8U);
293 
294  // allocate texture names:
295  m_glTextureName = getNewTextureNumber();
296 
297  // activate the texture unit first before binding texture
298  glActiveTexture(GL_TEXTURE0);
299  // select our current texture
300  glBindTexture(GL_TEXTURE_2D, m_glTextureName);
301  CHECK_OPENGL_ERROR();
302 
303  // when texture area is small, linear interpolation. Default is
304  // GL_LINEAR_MIPMAP_NEAREST but we
305  // are not building mipmaps.
306  // See also:
307  // http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=133116&page=1
308  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
309  CHECK_OPENGL_ERROR();
310 
311  // when texture area is large, NEAREST: this is mainly thinking of
312  // rendering
313  // occupancy grid maps, such as we want those "big pixels" to be
314  // clearly visible ;-)
315  glTexParameterf(
316  GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
317  m_textureInterpolate ? GL_LINEAR : GL_NEAREST);
318  CHECK_OPENGL_ERROR();
319 
320  // if wrap is true, the texture wraps over at the edges (repeat)
321  // ... false, the texture ends at the edges (clamp)
322  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
323  CHECK_OPENGL_ERROR();
324 
325  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
326  CHECK_OPENGL_ERROR();
327 
328  // Assure that the images do not overpass the maximum dimensions allowed
329  // by OpenGL:
330  // ------------------------------------------------------------------------------------
331  GLint texSize;
332  glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize);
333  while (m_textureImage.getHeight() > (unsigned int)texSize ||
334  m_textureImage.getWidth() > (unsigned int)texSize)
335  {
336  m_textureImage =
337  m_textureImage.scaleHalf(mrpt::img::IMG_INTERP_LINEAR);
338  m_textureImageAlpha =
339  m_textureImageAlpha.scaleHalf(mrpt::img::IMG_INTERP_LINEAR);
340  }
341 
342  const int width = m_textureImage.getWidth();
343  const int height = m_textureImage.getHeight();
344 
345 #ifdef TEXTUREOBJ_PROFILE_MEM_ALLOC
346  {
347  const std::string sSec = mrpt::format(
348  "opengl_texture: load %ix%i %s %stransp", width, height,
349  m_textureImage.isColor() ? "RGB" : "BW",
350  m_enableTransparency ? "" : "no ");
351  tim.enter(sSec.c_str());
352  }
353 #endif
354 
355  if (m_enableTransparency)
356  {
357  ASSERT_(!m_textureImageAlpha.isColor());
359  m_textureImageAlpha.getWidth(), m_textureImage.getWidth());
361  m_textureImageAlpha.getHeight(), m_textureImage.getHeight());
362  }
363 
364  // GL_LUMINANCE and GL_LUMINANCE_ALPHA were removed in OpenGL 3.1
365  // Convert grayscale images into color:
366  if (!m_textureImage.isColor())
367  m_textureImage = m_textureImage.colorImage();
368 
369  // Color texture:
370  if (m_enableTransparency)
371  {
372 // Color texture WITH trans.
373 // --------------------------------------
374 #ifdef TEXTUREOBJ_PROFILE_MEM_ALLOC
375  const std::string sSec = mrpt::format(
376  "opengl_texture_alloc %ix%i (color,trans)", width, height);
377  tim.enter(sSec.c_str());
378 #endif
379 
380  dataAligned = reserveDataBuffer(height * width * 4 + 512, data);
381 
382 #ifdef TEXTUREOBJ_PROFILE_MEM_ALLOC
383  tim.leave(sSec.c_str());
384 #endif
385 
386  for (int y = 0; y < height; y++)
387  {
388  unsigned char* ptrSrcCol = m_textureImage(0, y, 0);
389  unsigned char* ptrSrcAlfa = m_textureImageAlpha(0, y);
390  unsigned char* ptr = dataAligned + y * width * 4;
391 
392  for (int x = 0; x < width; x++)
393  {
394  *ptr++ = *ptrSrcCol++;
395  *ptr++ = *ptrSrcCol++;
396  *ptr++ = *ptrSrcCol++;
397  *ptr++ = *ptrSrcAlfa++;
398  }
399  }
400 
401  // Prepare image data types:
402  const GLenum img_type = GL_UNSIGNED_BYTE;
403  // Reverse RGB <-> BGR order?
404  const bool is_RGB_order =
405  (m_textureImage.getChannelsOrder() == std::string("RGB"));
406  const GLenum img_format = (is_RGB_order ? GL_RGBA : GL_BGRA);
407 
408  // Send image data to OpenGL:
409  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
410  glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
411  glTexImage2D(
412  GL_TEXTURE_2D, 0 /*level*/, GL_RGBA8 /* RGB components */,
413  width, height, 0 /*border*/, img_format, img_type, dataAligned);
414  CHECK_OPENGL_ERROR();
415  glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); // Reset
416  CHECK_OPENGL_ERROR();
417 
418  } // End of color texture WITH trans.
419  else
420  {
421  // Color texture WITHOUT trans.
422  // --------------------------------------
423  // Prepare image data types:
424  const GLenum img_type = GL_UNSIGNED_BYTE;
425  const int nBytesPerPixel = m_textureImage.isColor() ? 3 : 1;
426  // Reverse RGB <-> BGR order?
427  const bool is_RGB_order =
428  (m_textureImage.getChannelsOrder() == std::string("RGB"));
429  const GLenum img_format = nBytesPerPixel == 3
430  ? (is_RGB_order ? GL_RGB : GL_BGR)
431  : GL_LUMINANCE;
432 
433  // Send image data to OpenGL:
434  glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
435  CHECK_OPENGL_ERROR();
436  glPixelStorei(
437  GL_UNPACK_ROW_LENGTH,
438  m_textureImage.getRowStride() / nBytesPerPixel);
439  CHECK_OPENGL_ERROR();
440  glTexImage2D(
441  GL_TEXTURE_2D, 0 /*level*/, GL_RGB8 /* RGB components */, width,
442  height, 0 /*border*/, img_format, img_type,
443  m_textureImage.ptrLine<uint8_t>(0));
444  CHECK_OPENGL_ERROR();
445  glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); // Reset
446  CHECK_OPENGL_ERROR();
447 
448  } // End of color texture WITHOUT trans.
449 
450  m_texture_is_loaded = true;
451 
452 #ifdef TEXTUREOBJ_PROFILE_MEM_ALLOC
453  {
454  const std::string sSec = mrpt::format(
455  "opengl_texture: load %ix%i %s %stransp", width, height,
456  m_textureImage.isColor() ? "RGB" : "BW",
457  m_enableTransparency ? "" : "no ");
458  tim.leave(sSec.c_str());
459  }
460 #endif
461 
462 #ifdef TEXTUREOBJ_USE_MEMPOOL
463  // Before freeing the buffer in "data", donate my memory to the pool:
464  if (!data.empty())
465  {
467  if (pool)
468  {
469  CRenderizableShaderTexturedTriangles_MemPoolParams mem_params;
470  mem_params.len = data.size();
471 
472  auto* mem_block =
474  data.swap(mem_block->data);
475 
476  pool->dump_to_pool(mem_params, mem_block);
477  }
478  }
479 #endif
480  }
481  catch (exception& e)
482  {
484  format("m_glTextureName=%i\n%s", m_glTextureName, e.what()));
485  }
486  catch (...)
487  {
488  THROW_EXCEPTION("Runtime error!");
489  }
490 #endif
491 }
492 
494 {
495  try
496  {
497  unloadTexture();
498  }
499  catch (const std::exception& e)
500  {
501  std::cerr
502  << "[~CRenderizableShaderTexturedTriangles] Ignoring exception: "
504  }
505 }
507 {
508  if (m_texture_is_loaded)
509  {
510  m_texture_is_loaded = false;
511  releaseTextureName(m_glTextureName);
512  m_glTextureName = 0;
513  }
514 }
515 
518 {
519  uint8_t ver = 1;
520 
521  out << ver;
522  out << m_enableTransparency << m_textureInterpolate;
523  out << m_textureImage;
524  if (m_enableTransparency) out << m_textureImageAlpha;
525  out << m_textureImageAssigned;
526 }
527 
530 {
531  uint8_t version;
532  in >> version;
533 
535 
536  switch (version)
537  {
538  case 0:
539  case 1:
540  {
541  in >> m_enableTransparency >> m_textureInterpolate;
542  in >> m_textureImage;
543  if (m_enableTransparency)
544  {
545  in >> m_textureImageAlpha;
546  assignImage(m_textureImage, m_textureImageAlpha);
547  }
548  else
549  {
550  assignImage(m_textureImage);
551  }
552  if (version >= 1)
553  in >> m_textureImageAssigned;
554  else
555  m_textureImageAssigned = true;
556  }
557  break;
558  default:
560  };
562 }
563 
564 static std::map<unsigned int, std::thread::id> textureReservedFrom;
565 
567 {
568  MRPT_START
569 #if MRPT_HAS_OPENGL_GLUT
570  // Create one OpenGL texture
571  GLuint textureID;
572  glGenTextures(1, &textureID);
573 
574  textureReservedFrom[textureID] = std::this_thread::get_id();
575  return textureID;
576 #else
577  THROW_EXCEPTION("This function needs OpenGL");
578 #endif
579  MRPT_END
580 }
581 
583 {
584  MRPT_START
585 #if MRPT_HAS_OPENGL_GLUT
586 
587  auto it = textureReservedFrom.find(i);
588  if (it == textureReservedFrom.end()) return;
589 
590  if (std::this_thread::get_id() == it->second)
591  {
592  GLuint t = i;
593  glDeleteTextures(1, &t);
594  CHECK_OPENGL_ERROR();
595  }
596 
597  textureReservedFrom.erase(it);
598 #endif
599  MRPT_END
600 }
#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 2.0.2 Git: 9b4fd2465 Mon May 4 16:59:08 2020 +0200 at lun may 4 17:26:07 CEST 2020