08e7e101be16a4c82c512001b35f00601fadde9c
[occt.git] / src / OpenGl / OpenGl_Texture.cxx
1 // Created by: Kirill GAVRILOV
2 // Copyright (c) 2012 OPEN CASCADE SAS
3 //
4 // The content of this file is subject to the Open CASCADE Technology Public
5 // License Version 6.5 (the "License"). You may not use the content of this file
6 // except in compliance with the License. Please obtain a copy of the License
7 // at http://www.opencascade.org and read it completely before using this file.
8 //
9 // The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
10 // main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
11 //
12 // The Original Code and all software distributed under the License is
13 // distributed on an "AS IS" basis, without warranty of any kind, and the
14 // Initial Developer hereby disclaims all such warranties, including without
15 // limitation, any warranties of merchantability, fitness for a particular
16 // purpose or non-infringement. Please see the License for the specific terms
17 // and conditions governing the rights and limitations under the License.
18
19 #include <OpenGl_Texture.hxx>
20
21 #include <OpenGl_ExtFBO.hxx>
22 #include <OpenGl_Context.hxx>
23 #include <Graphic3d_TextureParams.hxx>
24 #include <Standard_Assert.hxx>
25 #include <Image_PixMap.hxx>
26
27 IMPLEMENT_STANDARD_HANDLE (OpenGl_Texture, OpenGl_Resource)
28 IMPLEMENT_STANDARD_RTTIEXT(OpenGl_Texture, OpenGl_Resource)
29
30 //! Function for getting power of to number larger or equal to input number.
31 //! @param theNumber    number to 'power of two'
32 //! @param theThreshold upper threshold
33 //! @return power of two number
34 inline GLsizei getPowerOfTwo (const GLsizei theNumber,
35                               const GLsizei theThreshold)
36 {
37   for (GLsizei p2 = 2; p2 <= theThreshold; p2 <<= 1)
38   {
39     if (theNumber <= p2)
40     {
41       return p2;
42     }
43   }
44   return theThreshold;
45 }
46
47 // =======================================================================
48 // function : OpenGl_Texture
49 // purpose  :
50 // =======================================================================
51 OpenGl_Texture::OpenGl_Texture (const Handle(Graphic3d_TextureParams)& theParams)
52 : OpenGl_Resource(),
53   myTextureId (NO_TEXTURE),
54   myTarget (GL_TEXTURE_2D),
55   mySizeX (0),
56   mySizeY (0),
57   myTextFormat (GL_FLOAT),
58   myHasMipmaps (Standard_False),
59   myParams     (theParams)
60 {
61   if (myParams.IsNull())
62   {
63     myParams = new Graphic3d_TextureParams();
64   }
65 }
66
67 // =======================================================================
68 // function : ~OpenGl_Texture
69 // purpose  :
70 // =======================================================================
71 OpenGl_Texture::~OpenGl_Texture()
72 {
73   Release (NULL);
74 }
75
76 // =======================================================================
77 // function : HasMipmaps
78 // purpose  :
79 // =======================================================================
80 const Standard_Boolean OpenGl_Texture::HasMipmaps() const
81 {
82   return myHasMipmaps;
83 }
84
85 // =======================================================================
86 // function : GetParams
87 // purpose  :
88 // =======================================================================
89 const Handle(Graphic3d_TextureParams)& OpenGl_Texture::GetParams() const
90 {
91   return myParams;
92 }
93
94 // =======================================================================
95 // function : SetParams
96 // purpose  :
97 // =======================================================================
98 void OpenGl_Texture::SetParams (const Handle(Graphic3d_TextureParams)& theParams)
99 {
100   myParams = theParams;
101 }
102
103 // =======================================================================
104 // function : Create
105 // purpose  :
106 // =======================================================================
107 bool OpenGl_Texture::Create (const Handle(OpenGl_Context)& )
108 {
109   if (myTextureId == NO_TEXTURE)
110   {
111     glGenTextures (1, &myTextureId);
112   }
113   return myTextureId != NO_TEXTURE;
114 }
115
116 // =======================================================================
117 // function : Release
118 // purpose  :
119 // =======================================================================
120 void OpenGl_Texture::Release (const OpenGl_Context* theGlCtx)
121 {
122   if (myTextureId == NO_TEXTURE)
123   {
124     return;
125   }
126
127   // application can not handle this case by exception - this is bug in code
128   Standard_ASSERT_RETURN (theGlCtx != NULL,
129     "OpenGl_Texture destroyed without GL context! Possible GPU memory leakage...",);
130
131   glDeleteTextures (1, &myTextureId);
132   myTextureId = NO_TEXTURE;
133   mySizeX = mySizeY = 0;
134 }
135
136 // =======================================================================
137 // function : Bind
138 // purpose  :
139 // =======================================================================
140 void OpenGl_Texture::Bind (const Handle(OpenGl_Context)& theCtx,
141                            const GLenum theTextureUnit) const
142 {
143   if (theCtx->IsGlGreaterEqual (1, 3))
144   {
145     theCtx->core13->glActiveTexture (theTextureUnit);
146   }
147   glBindTexture (myTarget, myTextureId);
148 }
149
150 // =======================================================================
151 // function : Unbind
152 // purpose  :
153 // =======================================================================
154 void OpenGl_Texture::Unbind (const Handle(OpenGl_Context)& theCtx,
155                              const GLenum theTextureUnit) const
156 {
157   if (theCtx->IsGlGreaterEqual (1, 3))
158   {
159     theCtx->core13->glActiveTexture (theTextureUnit);
160   }
161   glBindTexture (myTarget, NO_TEXTURE);
162 }
163
164 // =======================================================================
165 // function : Init
166 // purpose  :
167 // =======================================================================
168 bool OpenGl_Texture::Init (const Handle(OpenGl_Context)& theCtx,
169                            const Image_PixMap&           theImage,
170                            const Graphic3d_TypeOfTexture theType)
171 {
172   myHasMipmaps = Standard_False;
173   if (theImage.IsEmpty() || !Create (theCtx))
174   {
175     return false;
176   }
177
178   GLenum aTextureFormat = GL_RGBA8;
179   GLenum aPixelFormat   = 0;
180   GLenum aDataType      = 0;
181   switch (theImage.Format())
182   {
183     case Image_PixMap::ImgGrayF:
184     {
185       aTextureFormat = GL_ALPHA8; // GL_R8, GL_R32F
186       aPixelFormat   = GL_ALPHA;  // GL_RED
187       aDataType      = GL_FLOAT;
188       break;
189     }
190     case Image_PixMap::ImgRGBAF:
191     {
192       aTextureFormat = GL_RGBA8; // GL_RGBA32F
193       aPixelFormat   = GL_RGBA;
194       aDataType      = GL_FLOAT;
195       break;
196     }
197     case Image_PixMap::ImgBGRAF:
198     {
199       if (!theCtx->IsGlGreaterEqual (1, 2) && !theCtx->extBgra)
200       {
201         return false;
202       }
203       aTextureFormat = GL_RGBA8; // GL_RGBA32F
204       aPixelFormat   = GL_BGRA;  // equals to GL_BGRA_EXT
205       aDataType      = GL_FLOAT;
206       break;
207     }
208     case Image_PixMap::ImgRGBF:
209     {
210       aTextureFormat = GL_RGB8; // GL_RGB32F
211       aPixelFormat   = GL_RGB;
212       aDataType      = GL_FLOAT;
213       break;
214     }
215     case Image_PixMap::ImgBGRF:
216     {
217       aTextureFormat = GL_RGB8; // GL_RGB32F
218       aPixelFormat   = GL_BGR;  // equals to GL_BGR_EXT
219       aDataType      = GL_FLOAT;
220       break;
221     }
222     case Image_PixMap::ImgRGBA:
223     {
224       aTextureFormat = GL_RGBA8;
225       aPixelFormat   = GL_RGBA;
226       aDataType      = GL_UNSIGNED_BYTE;
227       break;
228     }
229     case Image_PixMap::ImgBGRA:
230     {
231       if (!theCtx->IsGlGreaterEqual (1, 2) && !theCtx->extBgra)
232       {
233         return false;
234       }
235       aTextureFormat = GL_RGBA8;
236       aPixelFormat   = GL_BGRA;  // equals to GL_BGRA_EXT
237       aDataType      = GL_UNSIGNED_BYTE;
238       break;
239     }
240     case Image_PixMap::ImgRGB32:
241     {
242       aTextureFormat = GL_RGB8;
243       aPixelFormat   = GL_RGBA;
244       aDataType      = GL_UNSIGNED_BYTE;
245       break;
246     }
247     case Image_PixMap::ImgBGR32:
248     {
249       if (!theCtx->IsGlGreaterEqual (1, 2) && !theCtx->extBgra)
250       {
251         return false;
252       }
253       aTextureFormat = GL_RGB8;
254       aPixelFormat   = GL_BGRA;  // equals to GL_BGRA_EXT
255       aDataType      = GL_UNSIGNED_BYTE;
256       break;
257     }
258     case Image_PixMap::ImgRGB:
259     {
260       aTextureFormat = GL_RGB8;
261       aPixelFormat   = GL_RGB;
262       aDataType      = GL_UNSIGNED_BYTE;
263       break;
264     }
265     case Image_PixMap::ImgBGR:
266     {
267       if (!theCtx->IsGlGreaterEqual (1, 2) && !theCtx->extBgra)
268       {
269         return false;
270       }
271       aTextureFormat = GL_RGB8;
272       aPixelFormat   = GL_BGR;  // equals to GL_BGR_EXT
273       aDataType      = GL_UNSIGNED_BYTE;
274       break;
275     }
276     case Image_PixMap::ImgGray:
277     {
278       aTextureFormat = GL_ALPHA8; // GL_R8
279       aPixelFormat   = GL_ALPHA;  // GL_RED
280       aDataType      = GL_UNSIGNED_BYTE;
281       break;
282     }
283     default:
284     {
285       return false;
286     }
287   }
288
289   const GLsizei aMaxSize   = theCtx->MaxTextureSize();
290   const GLsizei aWidth     = (GLsizei )theImage.SizeX();
291   const GLsizei aHeight    = (GLsizei )theImage.SizeY();
292
293   // Notice that formally general NPOT textures are required by OpenGL 2.0 specifications
294   // however some hardware (NV30 - GeForce FX, RadeOn 9xxx and Xxxx) supports GLSL but not NPOT!
295   // Trying to create NPOT rextures on such hardware will not fail
296   // but driver will fall back into software rendering,
297   const bool    toForceP2  = !theCtx->IsGlGreaterEqual (3, 0) && !theCtx->arbNPTW;
298   const GLsizei aWidthOut  = toForceP2 ? getPowerOfTwo (aWidth,  aMaxSize) : Min (aWidth,  aMaxSize);
299   const GLsizei aHeightOut = toForceP2 ? getPowerOfTwo (aHeight, aMaxSize) : Min (aHeight, aMaxSize);
300
301   GLint aTestWidth  = 0;
302   GLint aTestHeight = 0;
303
304   glPixelStorei (GL_UNPACK_ALIGNMENT, 1); // ensure alignment will not screw up the party
305   switch (theType)
306   {
307     case Graphic3d_TOT_1D:
308     {
309       myTarget = GL_TEXTURE_1D;
310       Bind (theCtx);
311       glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
312       glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
313
314       Image_PixMap aCopy;
315       GLvoid* aDataPtr = (GLvoid* )theImage.Data();
316       if (aWidth != aWidthOut)
317       {
318         if (!aCopy.InitTrash (theImage.Format(), Standard_Size(aWidthOut), 1)
319           || gluScaleImage (aPixelFormat,
320                             aWidth,    1, aDataType, theImage.Data(),
321                             aWidthOut, 1, aDataType, aCopy.ChangeData()) != 0)
322         {
323           Unbind (theCtx);
324           return false;
325         }
326
327         aDataPtr = (GLvoid* )aCopy.Data();
328       }
329
330       // use proxy to check texture could be created or not
331       glTexImage1D (GL_PROXY_TEXTURE_1D, 0, aTextureFormat,
332                     aWidthOut, 0,
333                     aPixelFormat, aDataType, NULL);
334       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &aTestWidth);
335       if (aTestWidth == 0)
336       {
337         // no memory or broken input parameters
338         Unbind (theCtx);
339         return false;
340       }
341
342       glTexImage1D (GL_TEXTURE_1D, 0, aTextureFormat,
343                     aWidthOut, 0,
344                     aPixelFormat, aDataType, aDataPtr);
345
346       Unbind (theCtx);
347       return true;
348     }
349     case Graphic3d_TOT_2D:
350     {
351       myTarget = GL_TEXTURE_2D;
352       Bind (theCtx);
353       glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
354       glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
355
356       Image_PixMap aCopy;
357       GLvoid* aDataPtr = (GLvoid* )theImage.Data();
358       if (aWidth != aWidthOut || aHeight != aHeightOut)
359       {
360         // scale texture
361         if (!aCopy.InitTrash (theImage.Format(), Standard_Size(aWidthOut), Standard_Size(aHeightOut))
362           || gluScaleImage (aPixelFormat,
363                             aWidth,    aHeight,    aDataType, theImage.Data(),
364                             aWidthOut, aHeightOut, aDataType, aCopy.ChangeData()) != 0)
365         {
366           Unbind (theCtx);
367           return false;
368         }
369
370         aDataPtr = (GLvoid* )aCopy.Data();
371       }
372
373       // use proxy to check texture could be created or not
374       glTexImage2D (GL_PROXY_TEXTURE_2D, 0, aTextureFormat,
375                     aWidthOut, aHeightOut, 0,
376                     aPixelFormat, aDataType, NULL);
377       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,  &aTestWidth);
378       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &aTestHeight);
379       if (aTestWidth == 0 || aTestHeight == 0)
380       {
381         // no memory or broken input parameters
382         Unbind (theCtx);
383         return false;
384       }
385
386       glTexImage2D (GL_TEXTURE_2D, 0, aTextureFormat,
387                     aWidthOut, aHeightOut, 0,
388                     aPixelFormat, aDataType, aDataPtr);
389
390       Unbind (theCtx);
391       return true;
392     }
393     case Graphic3d_TOT_2D_MIPMAP:
394     {
395       myTarget     = GL_TEXTURE_2D;
396       myHasMipmaps = Standard_True;
397       Bind (theCtx);
398       glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
399       glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
400
401       if (theCtx->extFBO != NULL
402        && aWidth == aWidthOut && aHeight == aHeightOut)
403       {
404         // use proxy to check texture could be created or not
405         glTexImage2D (GL_PROXY_TEXTURE_2D, 0, aTextureFormat,
406                       aWidthOut, aHeightOut, 0,
407                       aPixelFormat, aDataType, NULL);
408         glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,  &aTestWidth);
409         glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &aTestHeight);
410         if (aTestWidth == 0 || aTestHeight == 0)
411         {
412           // no memory or broken input parameters
413           Unbind (theCtx);
414           return false;
415         }
416
417         // upload main picture
418         glTexImage2D (GL_TEXTURE_2D, 0, aTextureFormat,
419                       aWidthOut, aHeightOut, 0,
420                       aPixelFormat, aDataType, theImage.Data());
421
422         // generate mipmaps
423         //glHint (GL_GENERATE_MIPMAP_HINT, GL_NICEST);
424         theCtx->extFBO->glGenerateMipmapEXT (GL_TEXTURE_2D);
425
426         Unbind (theCtx);
427         return true;
428       }
429       else
430       {
431         bool isCreated = gluBuild2DMipmaps (GL_TEXTURE_2D, aTextureFormat,
432                                             aWidth, aHeight,
433                                             aPixelFormat, aDataType, theImage.Data()) == 0;
434         Unbind (theCtx);
435         return isCreated;
436       }
437     }
438     default:
439     {
440       return false;
441     }
442   }
443 }