Radium Engine  1.5.0
Texture.cpp
1 #include <Core/Tasks/Task.hpp>
2 #include <Core/Tasks/TaskQueue.hpp>
3 #include <Core/Utils/Log.hpp>
4 #include <Engine/Data/Texture.hpp>
5 #include <Engine/RadiumEngine.hpp>
6 
7 #include <globjects/Texture.h>
8 
9 #include <cmath>
10 
11 namespace Ra {
12 namespace Engine {
13 namespace Data {
14 using namespace Core::Utils; // log
15 
16 Texture::Texture( const TextureParameters& texParameters ) :
17  m_textureParameters { texParameters },
18  m_texture { nullptr },
19  m_isMipMapped { false },
20  m_isLinear { false } {}
21 
23  if ( m_updateDataTaskId.isValid() ) {
24  RadiumEngine::getInstance()->removeGpuTask( m_updateDataTaskId );
25  }
26 }
27 
28 void Texture::initializeGL( bool linearize ) {
29  if ( ( m_textureParameters.target != GL_TEXTURE_1D ) &&
30  ( m_textureParameters.target != GL_TEXTURE_2D ) &&
31  ( m_textureParameters.target != GL_TEXTURE_RECTANGLE ) &&
32  ( m_textureParameters.target != GL_TEXTURE_3D ) &&
33  ( m_textureParameters.target != GL_TEXTURE_CUBE_MAP ) ) {
34  LOG( logERROR ) << "Texture of type " << m_textureParameters.target
35  << " must be generated explicitly!";
36  return;
37  }
38  // Transform texels if needed
39  if ( linearize ) {
40  uint numComp = 0;
41  bool hasAlpha = false;
42  switch ( m_textureParameters.format ) {
43  // RED and RG texture store a gray scale color. Verify if we need to convert
44  case GL_RED:
45  numComp = 1;
46  break;
47  case GL_RG:
48  // corresponds to deprecated GL_LUMINANCE_ALPHA
49  numComp = 2;
50  hasAlpha = true;
51  break;
52  case GL_RGB:
53  numComp = 3;
54  break;
55  case GL_RGBA:
56  numComp = 4;
57  hasAlpha = true;
58  break;
59  default:
60  LOG( logERROR ) << "Textures with format " << m_textureParameters.format
61  << " can't be linearized." << m_textureParameters.name;
62  return;
63  }
64  if ( m_textureParameters.target == GL_TEXTURE_CUBE_MAP ) {
65  linearizeCubeMap( numComp, hasAlpha );
66  }
67  else {
68  // This will only do do the RGB space conversion
69  sRGBToLinearRGB(
70  reinterpret_cast<uint8_t*>( m_textureParameters.texels ), numComp, hasAlpha );
71  }
72  }
73  // Generate OpenGL texture
74  if ( m_texture == nullptr ) {
75  m_texture = globjects::Texture::create( m_textureParameters.target );
76  GL_CHECK_ERROR;
77  }
78  // Update the sampler parameters
79  m_isMipMapped = !( m_textureParameters.minFilter == GL_NEAREST ||
80  m_textureParameters.minFilter == GL_LINEAR );
82  // upload texture to the GPU
83  updateGL();
84  // Generate mip-map if needed.
85  if ( m_isMipMapped ) { m_texture->generateMipmap(); }
86 }
87 
88 void Texture::bind( int unit ) {
89  if ( unit >= 0 ) { m_texture->bindActive( uint( unit ) ); }
90  else { m_texture->bind(); }
91 }
92 
94  GLint level,
95  GLboolean layered,
96  GLint layer,
97  GLenum access ) {
98  m_texture->bindImageTexture(
99  uint( unit ), level, layered, layer, access, m_textureParameters.internalFormat );
100 }
101 
102 void Texture::updateGL() {
103  CORE_ASSERT( m_texture != nullptr, "Cannot update non initialized texture" );
104  switch ( m_texture->target() ) {
105  case GL_TEXTURE_1D: {
106  m_texture->image1D( 0,
107  m_textureParameters.internalFormat,
108  GLsizei( m_textureParameters.width ),
109  0,
110  m_textureParameters.format,
111  m_textureParameters.type,
112  m_textureParameters.texels );
113  GL_CHECK_ERROR
114  } break;
115  case GL_TEXTURE_2D:
116  case GL_TEXTURE_RECTANGLE: {
117  m_texture->image2D( 0,
118  m_textureParameters.internalFormat,
119  GLsizei( m_textureParameters.width ),
120  GLsizei( m_textureParameters.height ),
121  0,
122  m_textureParameters.format,
123  m_textureParameters.type,
124  m_textureParameters.texels );
125  GL_CHECK_ERROR
126  } break;
127  case GL_TEXTURE_3D: {
128  m_texture->image3D( 0,
129  m_textureParameters.internalFormat,
130  GLsizei( m_textureParameters.width ),
131  GLsizei( m_textureParameters.height ),
132  GLsizei( m_textureParameters.depth ),
133  0,
134  m_textureParameters.format,
135  m_textureParameters.type,
136  m_textureParameters.texels );
137  GL_CHECK_ERROR
138  } break;
139  case GL_TEXTURE_CUBE_MAP: {
140  // Load the 6 faces of the cube-map
141  static const void* nullTexels[6] { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
142  auto texels = m_textureParameters.texels != nullptr
143  ? (const void**)m_textureParameters.texels
144  : nullTexels;
145 
146  m_texture->bind();
147  // track globjects updates that will hopefully support direct loading of
148  // cube-maps https://github.com/cginternals/globjects/issues/368
149  gl::glTexImage2D( gl::GL_TEXTURE_CUBE_MAP_POSITIVE_X,
150  0,
151  m_textureParameters.internalFormat,
152  GLsizei( m_textureParameters.width ),
153  GLsizei( m_textureParameters.height ),
154  0,
155  m_textureParameters.format,
156  m_textureParameters.type,
157  texels[0] );
158  gl::glTexImage2D( gl::GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
159  0,
160  m_textureParameters.internalFormat,
161  GLsizei( m_textureParameters.width ),
162  GLsizei( m_textureParameters.height ),
163  0,
164  m_textureParameters.format,
165  m_textureParameters.type,
166  texels[1] );
167 
168  gl::glTexImage2D( gl::GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
169  0,
170  m_textureParameters.internalFormat,
171  GLsizei( m_textureParameters.width ),
172  GLsizei( m_textureParameters.height ),
173  0,
174  m_textureParameters.format,
175  m_textureParameters.type,
176  texels[2] );
177  gl::glTexImage2D( gl::GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
178  0,
179  m_textureParameters.internalFormat,
180  GLsizei( m_textureParameters.width ),
181  GLsizei( m_textureParameters.height ),
182  0,
183  m_textureParameters.format,
184  m_textureParameters.type,
185  texels[3] );
186 
187  gl::glTexImage2D( gl::GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
188  0,
189  m_textureParameters.internalFormat,
190  GLsizei( m_textureParameters.width ),
191  GLsizei( m_textureParameters.height ),
192  0,
193  m_textureParameters.format,
194  m_textureParameters.type,
195  texels[4] );
196  gl::glTexImage2D( gl::GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
197  0,
198  m_textureParameters.internalFormat,
199  GLsizei( m_textureParameters.width ),
200  GLsizei( m_textureParameters.height ),
201  0,
202  m_textureParameters.format,
203  m_textureParameters.type,
204  texels[5] );
205 
206  m_texture->unbind();
207  GL_CHECK_ERROR
208  } break;
209  default: {
210  CORE_ASSERT( 0, "Unsupported texture type ?" );
211  } break;
212  }
213  GL_CHECK_ERROR;
214 }
215 
216 void Texture::updateData( void* newData ) {
217  // register gpu task to update opengl representation before next rendering
218  std::lock_guard<std::mutex> lock( m_updateMutex );
219 
220  m_textureParameters.texels = newData;
221 
222  if ( m_updateDataTaskId.isInvalid() ) {
223  auto taskFunc = [this]() {
224  std::lock_guard<std::mutex> taskLock( m_updateMutex );
225  this->updateGL();
226  m_updateDataTaskId = Core::TaskQueue::TaskId::Invalid();
227  };
228  auto task = std::make_unique<Core::FunctionTask>( taskFunc, getName() );
229  m_updateDataTaskId = RadiumEngine::getInstance()->addGpuTask( std::move( task ) );
230  }
231 }
232 
233 // let the compiler warn about case fallthrough
235  switch ( m_texture->target() ) {
236  case GL_TEXTURE_CUBE_MAP:
237  case GL_TEXTURE_3D:
238  m_texture->setParameter( GL_TEXTURE_WRAP_R, m_textureParameters.wrapP );
239  GL_CHECK_ERROR;
240  [[fallthrough]];
241  case GL_TEXTURE_2D:
242  case GL_TEXTURE_RECTANGLE:
243  m_texture->setParameter( GL_TEXTURE_WRAP_T, m_textureParameters.wrapT );
244  GL_CHECK_ERROR;
245  [[fallthrough]];
246  case GL_TEXTURE_1D:
247  m_texture->setParameter( GL_TEXTURE_WRAP_S, m_textureParameters.wrapS );
248  GL_CHECK_ERROR;
249  break;
250  default:
251  break;
252  }
253  m_texture->setParameter( GL_TEXTURE_MIN_FILTER, m_textureParameters.minFilter );
254  GL_CHECK_ERROR;
255  m_texture->setParameter( GL_TEXTURE_MAG_FILTER, m_textureParameters.magFilter );
256  GL_CHECK_ERROR;
257 }
258 
260  if ( m_texture != nullptr ) {
261  LOG( logERROR ) << "Only non OpenGL initialized texture can be linearized.";
262  return;
263  }
264  // Only RGB and RGBA texture contains color information
265  // (others are not really colors and must be managed explicitly by the user)
266  uint numComp = 0;
267  bool hasAlpha = false;
268  switch ( m_textureParameters.format ) {
269  // RED texture store a gray scale color. Verify if we need to convert
270  case GL_RED:
271  numComp = 1;
272  break;
273  case GL_RGB:
274  numComp = 3;
275  break;
276  case GL_RGBA:
277  numComp = 4;
278  hasAlpha = true;
279  break;
280  default:
281  LOG( logERROR ) << "Textures with format " << m_textureParameters.format
282  << " can't be linearized." << m_textureParameters.name;
283  return;
284  }
285  sRGBToLinearRGB( reinterpret_cast<uint8_t*>( m_textureParameters.texels ), numComp, hasAlpha );
286 }
287 
288 void Texture::sRGBToLinearRGB( uint8_t* texels, uint numComponent, bool hasAlphaChannel ) {
289  std::lock_guard<std::mutex> lock( m_updateMutex );
290  if ( !m_isLinear ) {
291  m_isLinear = true;
292  // auto linearize = [gamma](float in)-> float {
293  auto linearize = []( uint8_t in ) -> uint8_t {
294  // Constants are described at https://en.wikipedia.org/wiki/SRGB
295  float c = float( in ) / 255;
296  if ( c < 0.04045 ) { c = c / 12.92f; }
297  else { c = std::pow( ( ( c + 0.055f ) / ( 1.055f ) ), 2.4f ); }
298  return uint8_t( c * 255 );
299  };
300  uint numValues = hasAlphaChannel ? numComponent - 1 : numComponent;
301 #pragma omp parallel for
302  for ( int i = 0; i < int( m_textureParameters.width * m_textureParameters.height *
303  m_textureParameters.depth );
304  ++i ) {
305  // Convert each R or RGB value while keeping alpha unchanged
306  for ( uint p = i * numComponent; p < i * numComponent + numValues; ++p ) {
307  texels[p] = linearize( texels[p] );
308  }
309  }
310  }
311 }
312 
313 void Texture::resize( size_t w, size_t h, size_t d, void* pix ) {
314  m_textureParameters.width = w;
315  m_textureParameters.height = h;
316  m_textureParameters.depth = d;
317  m_textureParameters.texels = pix;
318  if ( m_texture == nullptr ) { initializeGL( false ); }
319  else { updateGL(); }
320  if ( m_isMipMapped ) { m_texture->generateMipmap(); }
321 }
322 
323 void Texture::linearizeCubeMap( uint numComponent, bool hasAlphaChannel ) {
324  if ( m_textureParameters.type == gl::GLenum::GL_UNSIGNED_BYTE ) {
327  for ( int i = 0; i < 6; ++i ) {
328  sRGBToLinearRGB(
329  reinterpret_cast<uint8_t*>( ( (void**)m_textureParameters.texels )[i] ),
330  numComponent,
331  hasAlphaChannel );
332  }
333  }
334 }
335 
336 } // namespace Data
337 } // namespace Engine
338 } // namespace Ra
void bind(int unit=-1)
Bind the texture to enable its use in a shader.
Definition: Texture.cpp:88
void updateData(void *newData)
Definition: Texture.cpp:216
void initializeGL(bool linearize=false)
Generate the OpenGL representation of the texture according to the stored TextureData.
Definition: Texture.cpp:28
std::string getName() const
Definition: Texture.hpp:151
void resize(size_t w=1, size_t h=1, size_t d=1, void *pix=nullptr)
Definition: Texture.cpp:313
void bindImageTexture(int unit, GLint level, GLboolean layered, GLint layer, GLenum access)
Bind the texture to an image unit for the purpose of reading and writing it from shaders.
Definition: Texture.cpp:93
Texture(const Texture &)=delete
Definition: Cage.cpp:3
GLenum wrapT
OpenGL wrap mode in the t direction.
Definition: Texture.hpp:62
GLenum magFilter
OpenGL magnification filter ( GL_LINEAR or GL_NEAREST )
Definition: Texture.hpp:68
GLenum wrapS
OpenGL wrap mode in the s direction.
Definition: Texture.hpp:60
size_t depth
width of the texture (p dimension)
Definition: Texture.hpp:52
GLenum wrapP
OpenGL wrap mode in the p direction.
Definition: Texture.hpp:64
size_t height
height of the texture (t dimension)
Definition: Texture.hpp:50
GLenum minFilter
OpenGL minification filter ( GL_LINEAR or GL_NEAREST or GL_XXX_MIPMAP_YYY )
Definition: Texture.hpp:66
GLenum format
Format of the external data.
Definition: Texture.hpp:54
size_t width
width of the texture (s dimension)
Definition: Texture.hpp:48
GLenum target
OpenGL target.
Definition: Texture.hpp:46
GLenum type
Type of the components in external data.
Definition: Texture.hpp:58
GLenum internalFormat
OpenGL internal format (WARNING, for Integer textures, must be GL_XXX_INTEGER)
Definition: Texture.hpp:56
std::string name
Name of the texture.
Definition: Texture.hpp:44