1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
2 // This file is part of the "Irrlicht Engine".
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
4
5 #include "IrrCompileConfig.h"
6
7 #ifdef _IRR_COMPILE_WITH_OPENGL_
8
9 #include "irrTypes.h"
10 #include "COpenGLTexture.h"
11 #include "COpenGLDriver.h"
12 #include "os.h"
13 #include "CColorConverter.h"
14
15 #include "irrString.h"
16
17 namespace irr
18 {
19 namespace video
20 {
21
22 //! constructor for usual textures
COpenGLTexture(IImage * origImage,const io::path & name,void * mipmapData,COpenGLDriver * driver)23 COpenGLTexture::COpenGLTexture(IImage* origImage, const io::path& name, void* mipmapData, COpenGLDriver* driver)
24 : ITexture(name), ColorFormat(ECF_A8R8G8B8), Driver(driver), Image(0), MipImage(0),
25 TextureName(0), InternalFormat(GL_RGBA), PixelFormat(GL_BGRA_EXT),
26 PixelType(GL_UNSIGNED_BYTE), MipLevelStored(0), MipmapLegacyMode(true),
27 IsRenderTarget(false), AutomaticMipmapUpdate(false),
28 ReadOnlyLock(false), KeepImage(true)
29 {
30 #ifdef _DEBUG
31 setDebugName("COpenGLTexture");
32 #endif
33
34 HasMipMaps = Driver->getTextureCreationFlag(ETCF_CREATE_MIP_MAPS);
35 getImageValues(origImage);
36
37 glGenTextures(1, &TextureName);
38
39 if (ImageSize==TextureSize)
40 {
41 Image = Driver->createImage(ColorFormat, ImageSize);
42 origImage->copyTo(Image);
43 }
44 else
45 {
46 Image = Driver->createImage(ColorFormat, TextureSize);
47 // scale texture
48 origImage->copyToScaling(Image);
49 }
50 uploadTexture(true, mipmapData);
51 if (!KeepImage)
52 {
53 Image->drop();
54 Image=0;
55 }
56 }
57
58
59 //! constructor for basic setup (only for derived classes)
COpenGLTexture(const io::path & name,COpenGLDriver * driver)60 COpenGLTexture::COpenGLTexture(const io::path& name, COpenGLDriver* driver)
61 : ITexture(name), ColorFormat(ECF_A8R8G8B8), Driver(driver), Image(0), MipImage(0),
62 TextureName(0), InternalFormat(GL_RGBA), PixelFormat(GL_BGRA_EXT),
63 PixelType(GL_UNSIGNED_BYTE), MipLevelStored(0), HasMipMaps(true),
64 MipmapLegacyMode(true), IsRenderTarget(false), AutomaticMipmapUpdate(false),
65 ReadOnlyLock(false), KeepImage(true)
66 {
67 #ifdef _DEBUG
68 setDebugName("COpenGLTexture");
69 #endif
70 }
71
72
73 //! destructor
~COpenGLTexture()74 COpenGLTexture::~COpenGLTexture()
75 {
76 if (TextureName)
77 glDeleteTextures(1, &TextureName);
78 if (Image)
79 Image->drop();
80 }
81
82
83 //! Choose best matching color format, based on texture creation flags
getBestColorFormat(ECOLOR_FORMAT format)84 ECOLOR_FORMAT COpenGLTexture::getBestColorFormat(ECOLOR_FORMAT format)
85 {
86 ECOLOR_FORMAT destFormat = ECF_A8R8G8B8;
87 switch (format)
88 {
89 case ECF_A1R5G5B5:
90 if (!Driver->getTextureCreationFlag(ETCF_ALWAYS_32_BIT))
91 destFormat = ECF_A1R5G5B5;
92 break;
93 case ECF_R5G6B5:
94 if (!Driver->getTextureCreationFlag(ETCF_ALWAYS_32_BIT))
95 destFormat = ECF_A1R5G5B5;
96 break;
97 case ECF_A8R8G8B8:
98 if (Driver->getTextureCreationFlag(ETCF_ALWAYS_16_BIT) ||
99 Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_SPEED))
100 destFormat = ECF_A1R5G5B5;
101 break;
102 case ECF_R8G8B8:
103 if (Driver->getTextureCreationFlag(ETCF_ALWAYS_16_BIT) ||
104 Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_SPEED))
105 destFormat = ECF_A1R5G5B5;
106 default:
107 break;
108 }
109 if (Driver->getTextureCreationFlag(ETCF_NO_ALPHA_CHANNEL))
110 {
111 switch (destFormat)
112 {
113 case ECF_A1R5G5B5:
114 destFormat = ECF_R5G6B5;
115 break;
116 case ECF_A8R8G8B8:
117 destFormat = ECF_R8G8B8;
118 break;
119 default:
120 break;
121 }
122 }
123 return destFormat;
124 }
125
126
127 //! Get opengl values for the GPU texture storage
getOpenGLFormatAndParametersFromColorFormat(ECOLOR_FORMAT format,GLint & filtering,GLenum & colorformat,GLenum & type)128 GLint COpenGLTexture::getOpenGLFormatAndParametersFromColorFormat(ECOLOR_FORMAT format,
129 GLint& filtering,
130 GLenum& colorformat,
131 GLenum& type)
132 {
133 // default
134 filtering = GL_LINEAR;
135 colorformat = GL_RGBA;
136 type = GL_UNSIGNED_BYTE;
137 GLenum internalformat = GL_RGBA;
138
139 switch(format)
140 {
141 case ECF_A1R5G5B5:
142 colorformat=GL_BGRA_EXT;
143 type=GL_UNSIGNED_SHORT_1_5_5_5_REV;
144 internalformat = GL_RGBA;
145 break;
146 case ECF_R5G6B5:
147 colorformat=GL_RGB;
148 type=GL_UNSIGNED_SHORT_5_6_5;
149 internalformat = GL_RGB;
150 break;
151 case ECF_R8G8B8:
152 colorformat=GL_BGR;
153 type=GL_UNSIGNED_BYTE;
154 internalformat = GL_RGB;
155 break;
156 case ECF_A8R8G8B8:
157 colorformat=GL_BGRA_EXT;
158 if (Driver->Version > 101)
159 type=GL_UNSIGNED_INT_8_8_8_8_REV;
160 internalformat = GL_RGBA;
161 break;
162 // Floating Point texture formats. Thanks to Patryk "Nadro" Nadrowski.
163 case ECF_R16F:
164 {
165 #ifdef GL_ARB_texture_rg
166 filtering = GL_NEAREST;
167 colorformat = GL_RED;
168 type = GL_FLOAT;
169
170 internalformat = GL_R16F;
171 #else
172 ColorFormat = ECF_A8R8G8B8;
173 internalformat = GL_RGB8;
174 #endif
175 }
176 break;
177 case ECF_G16R16F:
178 {
179 #ifdef GL_ARB_texture_rg
180 filtering = GL_NEAREST;
181 colorformat = GL_RG;
182 type = GL_FLOAT;
183
184 internalformat = GL_RG16F;
185 #else
186 ColorFormat = ECF_A8R8G8B8;
187 internalformat = GL_RGB8;
188 #endif
189 }
190 break;
191 case ECF_A16B16G16R16F:
192 {
193 #ifdef GL_ARB_texture_rg
194 filtering = GL_NEAREST;
195 colorformat = GL_RGBA;
196 type = GL_FLOAT;
197
198 internalformat = GL_RGBA16F_ARB;
199 #else
200 ColorFormat = ECF_A8R8G8B8;
201 internalformat = GL_RGBA8;
202 #endif
203 }
204 break;
205 case ECF_R32F:
206 {
207 #ifdef GL_ARB_texture_rg
208 filtering = GL_NEAREST;
209 colorformat = GL_RED;
210 type = GL_FLOAT;
211
212 internalformat = GL_R32F;
213 #else
214 ColorFormat = ECF_A8R8G8B8;
215 internalformat = GL_RGB8;
216 #endif
217 }
218 break;
219 case ECF_G32R32F:
220 {
221 #ifdef GL_ARB_texture_rg
222 filtering = GL_NEAREST;
223 colorformat = GL_RG;
224 type = GL_FLOAT;
225
226 internalformat = GL_RG32F;
227 #else
228 ColorFormat = ECF_A8R8G8B8;
229 internalformat = GL_RGB8;
230 #endif
231 }
232 break;
233 case ECF_A32B32G32R32F:
234 {
235 #ifdef GL_ARB_texture_float
236 filtering = GL_NEAREST;
237 colorformat = GL_RGBA;
238 type = GL_FLOAT;
239
240 internalformat = GL_RGBA32F_ARB;
241 #else
242 ColorFormat = ECF_A8R8G8B8;
243 internalformat = GL_RGBA8;
244 #endif
245 }
246 break;
247 default:
248 {
249 os::Printer::log("Unsupported texture format", ELL_ERROR);
250 internalformat = GL_RGBA8;
251 }
252 }
253 #if defined(GL_ARB_framebuffer_sRGB) || defined(GL_EXT_framebuffer_sRGB)
254 if (Driver->Params.HandleSRGB)
255 {
256 if (internalformat==GL_RGBA)
257 internalformat=GL_SRGB_ALPHA_EXT;
258 else if (internalformat==GL_RGB)
259 internalformat=GL_SRGB_EXT;
260 }
261 #endif
262 return internalformat;
263 }
264
265
266 // prepare values ImageSize, TextureSize, and ColorFormat based on image
getImageValues(IImage * image)267 void COpenGLTexture::getImageValues(IImage* image)
268 {
269 if (!image)
270 {
271 os::Printer::log("No image for OpenGL texture.", ELL_ERROR);
272 return;
273 }
274
275 ImageSize = image->getDimension();
276
277 if ( !ImageSize.Width || !ImageSize.Height)
278 {
279 os::Printer::log("Invalid size of image for OpenGL Texture.", ELL_ERROR);
280 return;
281 }
282
283 const f32 ratio = (f32)ImageSize.Width/(f32)ImageSize.Height;
284 if ((ImageSize.Width>Driver->MaxTextureSize) && (ratio >= 1.0f))
285 {
286 ImageSize.Width = Driver->MaxTextureSize;
287 ImageSize.Height = (u32)(Driver->MaxTextureSize/ratio);
288 }
289 else if (ImageSize.Height>Driver->MaxTextureSize)
290 {
291 ImageSize.Height = Driver->MaxTextureSize;
292 ImageSize.Width = (u32)(Driver->MaxTextureSize*ratio);
293 }
294 TextureSize=ImageSize.getOptimalSize(!Driver->queryFeature(EVDF_TEXTURE_NPOT));
295
296 ColorFormat = getBestColorFormat(image->getColorFormat());
297 }
298
299
300 //! copies the the texture into an open gl texture.
uploadTexture(bool newTexture,void * mipmapData,u32 level)301 void COpenGLTexture::uploadTexture(bool newTexture, void* mipmapData, u32 level)
302 {
303 // check which image needs to be uploaded
304 IImage* image = level?MipImage:Image;
305 if (!image)
306 {
307 os::Printer::log("No image for OpenGL texture to upload", ELL_ERROR);
308 return;
309 }
310
311 // get correct opengl color data values
312 GLenum oldInternalFormat = InternalFormat;
313 GLint filtering;
314 InternalFormat = getOpenGLFormatAndParametersFromColorFormat(ColorFormat, filtering, PixelFormat, PixelType);
315 // make sure we don't change the internal format of existing images
316 if (!newTexture)
317 InternalFormat=oldInternalFormat;
318
319 Driver->setActiveTexture(0, this);
320 if (Driver->testGLError())
321 os::Printer::log("Could not bind Texture", ELL_ERROR);
322
323 // mipmap handling for main texture
324 if (!level && newTexture)
325 {
326 #ifndef DISABLE_MIPMAPPING
327 #ifdef GL_SGIS_generate_mipmap
328 // auto generate if possible and no mipmap data is given
329 if (HasMipMaps && !mipmapData && Driver->queryFeature(EVDF_MIP_MAP_AUTO_UPDATE))
330 {
331 if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_SPEED))
332 glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_FASTEST);
333 else if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_QUALITY))
334 glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST);
335 else
336 glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_DONT_CARE);
337
338 AutomaticMipmapUpdate=true;
339
340 if (!Driver->queryFeature(EVDF_FRAMEBUFFER_OBJECT))
341 {
342 glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE );
343 MipmapLegacyMode=true;
344 }
345 else
346 MipmapLegacyMode=false;
347 }
348 else
349 #endif
350 {
351 // Either generate manually due to missing capability
352 // or use predefined mipmap data
353 AutomaticMipmapUpdate=false;
354 regenerateMipMapLevels(mipmapData);
355 }
356 if (HasMipMaps) // might have changed in regenerateMipMapLevels
357 {
358 // enable bilinear mipmap filter
359 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST );
360 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
361 }
362 else
363 #else
364 HasMipMaps=false;
365 os::Printer::log("Did not create OpenGL texture mip maps.", ELL_INFORMATION);
366 #endif
367 {
368 // enable bilinear filter without mipmaps
369 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
370 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
371 }
372 }
373
374 // now get image data and upload to GPU
375 void* source = image->lock();
376 if (newTexture)
377 glTexImage2D(GL_TEXTURE_2D, level, InternalFormat, image->getDimension().Width,
378 image->getDimension().Height, 0, PixelFormat, PixelType, source);
379 else
380 glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, image->getDimension().Width,
381 image->getDimension().Height, PixelFormat, PixelType, source);
382 image->unlock();
383
384 if (!MipmapLegacyMode && AutomaticMipmapUpdate)
385 {
386 glEnable(GL_TEXTURE_2D);
387 Driver->extGlGenerateMipmap(GL_TEXTURE_2D);
388 }
389
390 if (Driver->testGLError())
391 os::Printer::log("Could not glTexImage2D", ELL_ERROR);
392 }
393
394
395 //! lock function
lock(E_TEXTURE_LOCK_MODE mode,u32 mipmapLevel)396 void* COpenGLTexture::lock(E_TEXTURE_LOCK_MODE mode, u32 mipmapLevel)
397 {
398 // store info about which image is locked
399 IImage* image = (mipmapLevel==0)?Image:MipImage;
400 ReadOnlyLock |= (mode==ETLM_READ_ONLY);
401 MipLevelStored = mipmapLevel;
402 if (!ReadOnlyLock && mipmapLevel)
403 {
404 #ifdef GL_SGIS_generate_mipmap
405 if (Driver->queryFeature(EVDF_MIP_MAP_AUTO_UPDATE))
406 {
407 // do not automatically generate and update mipmaps
408 glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);
409 }
410 #endif
411 AutomaticMipmapUpdate=false;
412 }
413
414 // if data not available or might have changed on GPU download it
415 if (!image || IsRenderTarget)
416 {
417 // prepare the data storage if necessary
418 if (!image)
419 {
420 if (mipmapLevel)
421 {
422 u32 i=0;
423 u32 width = TextureSize.Width;
424 u32 height = TextureSize.Height;
425 do
426 {
427 if (width>1)
428 width>>=1;
429 if (height>1)
430 height>>=1;
431 ++i;
432 }
433 while (i != mipmapLevel);
434 MipImage = image = Driver->createImage(ECF_A8R8G8B8, core::dimension2du(width,height));
435 }
436 else
437 Image = image = Driver->createImage(ECF_A8R8G8B8, ImageSize);
438 ColorFormat = ECF_A8R8G8B8;
439 }
440 if (!image)
441 return 0;
442
443 if (mode != ETLM_WRITE_ONLY)
444 {
445 u8* pixels = static_cast<u8*>(image->lock());
446 if (!pixels)
447 return 0;
448
449 // we need to keep the correct texture bound later on
450 GLint tmpTexture;
451 glGetIntegerv(GL_TEXTURE_BINDING_2D, &tmpTexture);
452 glBindTexture(GL_TEXTURE_2D, TextureName);
453
454 // we need to flip textures vertical
455 // however, it seems that this does not hold for mipmap
456 // textures, for unknown reasons.
457
458 // allows to read pixels in top-to-bottom order
459 #ifdef GL_MESA_pack_invert
460 if (!mipmapLevel && Driver->queryOpenGLFeature(COpenGLExtensionHandler::IRR_MESA_pack_invert))
461 glPixelStorei(GL_PACK_INVERT_MESA, GL_TRUE);
462 #endif
463
464 // download GPU data as ARGB8 to pixels;
465 glGetTexImage(GL_TEXTURE_2D, mipmapLevel, GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels);
466
467 if (!mipmapLevel)
468 {
469 #ifdef GL_MESA_pack_invert
470 if (Driver->queryOpenGLFeature(COpenGLExtensionHandler::IRR_MESA_pack_invert))
471 glPixelStorei(GL_PACK_INVERT_MESA, GL_FALSE);
472 else
473 #endif
474 {
475 // opengl images are horizontally flipped, so we have to fix that here.
476 const s32 pitch=image->getPitch();
477 u8* p2 = pixels + (image->getDimension().Height - 1) * pitch;
478 u8* tmpBuffer = new u8[pitch];
479 for (u32 i=0; i < image->getDimension().Height; i += 2)
480 {
481 memcpy(tmpBuffer, pixels, pitch);
482 memcpy(pixels, p2, pitch);
483 memcpy(p2, tmpBuffer, pitch);
484 pixels += pitch;
485 p2 -= pitch;
486 }
487 delete [] tmpBuffer;
488 }
489 }
490 image->unlock();
491
492 //reset old bound texture
493 glBindTexture(GL_TEXTURE_2D, tmpTexture);
494 }
495 }
496 return image->lock();
497 }
498
499
500 //! unlock function
unlock()501 void COpenGLTexture::unlock()
502 {
503 // test if miplevel or main texture was locked
504 IImage* image = MipImage?MipImage:Image;
505 if (!image)
506 return;
507 // unlock image to see changes
508 image->unlock();
509 // copy texture data to GPU
510 if (!ReadOnlyLock)
511 uploadTexture(false, 0, MipLevelStored);
512 ReadOnlyLock = false;
513 // cleanup local image
514 if (MipImage)
515 {
516 MipImage->drop();
517 MipImage=0;
518 }
519 else if (!KeepImage)
520 {
521 Image->drop();
522 Image=0;
523 }
524 // update information
525 if (Image)
526 ColorFormat=Image->getColorFormat();
527 else
528 ColorFormat=ECF_A8R8G8B8;
529 }
530
531
532 //! Returns size of the original image.
getOriginalSize() const533 const core::dimension2d<u32>& COpenGLTexture::getOriginalSize() const
534 {
535 return ImageSize;
536 }
537
538
539 //! Returns size of the texture.
getSize() const540 const core::dimension2d<u32>& COpenGLTexture::getSize() const
541 {
542 return TextureSize;
543 }
544
545
546 //! returns driver type of texture, i.e. the driver, which created the texture
getDriverType() const547 E_DRIVER_TYPE COpenGLTexture::getDriverType() const
548 {
549 return EDT_OPENGL;
550 }
551
552
553 //! returns color format of texture
getColorFormat() const554 ECOLOR_FORMAT COpenGLTexture::getColorFormat() const
555 {
556 return ColorFormat;
557 }
558
559
560 //! returns pitch of texture (in bytes)
getPitch() const561 u32 COpenGLTexture::getPitch() const
562 {
563 if (Image)
564 return Image->getPitch();
565 else
566 return 0;
567 }
568
569
570 //! return open gl texture name
getOpenGLTextureName() const571 GLuint COpenGLTexture::getOpenGLTextureName() const
572 {
573 return TextureName;
574 }
575
576
577 //! Returns whether this texture has mipmaps
hasMipMaps() const578 bool COpenGLTexture::hasMipMaps() const
579 {
580 return HasMipMaps;
581 }
582
583
584 //! Regenerates the mip map levels of the texture. Useful after locking and
585 //! modifying the texture
regenerateMipMapLevels(void * mipmapData)586 void COpenGLTexture::regenerateMipMapLevels(void* mipmapData)
587 {
588 if (AutomaticMipmapUpdate || !HasMipMaps || !Image)
589 return;
590 if ((Image->getDimension().Width==1) && (Image->getDimension().Height==1))
591 return;
592
593 // Manually create mipmaps or use prepared version
594 u32 width=Image->getDimension().Width;
595 u32 height=Image->getDimension().Height;
596 u32 i=0;
597 u8* target = static_cast<u8*>(mipmapData);
598 do
599 {
600 if (width>1)
601 width>>=1;
602 if (height>1)
603 height>>=1;
604 ++i;
605 if (!target)
606 target = new u8[width*height*Image->getBytesPerPixel()];
607 // create scaled version if no mipdata available
608 if (!mipmapData)
609 Image->copyToScaling(target, width, height, Image->getColorFormat());
610 glTexImage2D(GL_TEXTURE_2D, i, InternalFormat, width, height,
611 0, PixelFormat, PixelType, target);
612 // get next prepared mipmap data if available
613 if (mipmapData)
614 {
615 mipmapData = static_cast<u8*>(mipmapData)+width*height*Image->getBytesPerPixel();
616 target = static_cast<u8*>(mipmapData);
617 }
618 }
619 while (width!=1 || height!=1);
620 // cleanup
621 if (!mipmapData)
622 delete [] target;
623 }
624
625
isRenderTarget() const626 bool COpenGLTexture::isRenderTarget() const
627 {
628 return IsRenderTarget;
629 }
630
631
setIsRenderTarget(bool isTarget)632 void COpenGLTexture::setIsRenderTarget(bool isTarget)
633 {
634 IsRenderTarget = isTarget;
635 }
636
637
isFrameBufferObject() const638 bool COpenGLTexture::isFrameBufferObject() const
639 {
640 return false;
641 }
642
643
644 //! Bind Render Target Texture
bindRTT()645 void COpenGLTexture::bindRTT()
646 {
647 }
648
649
650 //! Unbind Render Target Texture
unbindRTT()651 void COpenGLTexture::unbindRTT()
652 {
653 Driver->setActiveTexture(0, this);
654
655 // Copy Our ViewPort To The Texture
656 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, getSize().Width, getSize().Height);
657 }
658
659
660 /* FBO Textures */
661
662 // helper function for render to texture
663 static bool checkFBOStatus(COpenGLDriver* Driver);
664
665 //! RTT ColorFrameBuffer constructor
COpenGLFBOTexture(const core::dimension2d<u32> & size,const io::path & name,COpenGLDriver * driver,ECOLOR_FORMAT format)666 COpenGLFBOTexture::COpenGLFBOTexture(const core::dimension2d<u32>& size,
667 const io::path& name, COpenGLDriver* driver,
668 ECOLOR_FORMAT format)
669 : COpenGLTexture(name, driver), DepthTexture(0), ColorFrameBuffer(0)
670 {
671 #ifdef _DEBUG
672 setDebugName("COpenGLTexture_FBO");
673 #endif
674
675 ImageSize = size;
676 TextureSize = size;
677
678 if (ECF_UNKNOWN == format)
679 format = getBestColorFormat(driver->getColorFormat());
680
681 ColorFormat = format;
682
683 GLint FilteringType;
684 InternalFormat = getOpenGLFormatAndParametersFromColorFormat(format, FilteringType, PixelFormat, PixelType);
685
686 HasMipMaps = false;
687 IsRenderTarget = true;
688
689 #ifdef GL_EXT_framebuffer_object
690 // generate frame buffer
691 Driver->extGlGenFramebuffers(1, &ColorFrameBuffer);
692 bindRTT();
693
694 // generate color texture
695 glGenTextures(1, &TextureName);
696 Driver->setActiveTexture(0, this);
697 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, FilteringType);
698 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
699 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
700 glTexImage2D(GL_TEXTURE_2D, 0, InternalFormat, ImageSize.Width,
701 ImageSize.Height, 0, PixelFormat, PixelType, 0);
702 #ifdef _DEBUG
703 driver->testGLError();
704 #endif
705
706 // attach color texture to frame buffer
707 Driver->extGlFramebufferTexture2D(GL_FRAMEBUFFER_EXT,
708 GL_COLOR_ATTACHMENT0_EXT,
709 GL_TEXTURE_2D,
710 TextureName,
711 0);
712 #ifdef _DEBUG
713 checkFBOStatus(Driver);
714 #endif
715
716 #endif
717 unbindRTT();
718 }
719
720
721 //! destructor
~COpenGLFBOTexture()722 COpenGLFBOTexture::~COpenGLFBOTexture()
723 {
724 if (DepthTexture)
725 if (DepthTexture->drop())
726 Driver->removeDepthTexture(DepthTexture);
727 if (ColorFrameBuffer)
728 Driver->extGlDeleteFramebuffers(1, &ColorFrameBuffer);
729 }
730
731
isFrameBufferObject() const732 bool COpenGLFBOTexture::isFrameBufferObject() const
733 {
734 return true;
735 }
736
737
738 //! Bind Render Target Texture
bindRTT()739 void COpenGLFBOTexture::bindRTT()
740 {
741 #ifdef GL_EXT_framebuffer_object
742 if (ColorFrameBuffer != 0)
743 Driver->extGlBindFramebuffer(GL_FRAMEBUFFER_EXT, ColorFrameBuffer);
744 glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
745 #endif
746 }
747
748
749 //! Unbind Render Target Texture
unbindRTT()750 void COpenGLFBOTexture::unbindRTT()
751 {
752 #ifdef GL_EXT_framebuffer_object
753 if (ColorFrameBuffer != 0)
754 Driver->extGlBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
755 #endif
756 }
757
758
759 /* FBO Depth Textures */
760
761 //! RTT DepthBuffer constructor
COpenGLFBODepthTexture(const core::dimension2d<u32> & size,const io::path & name,COpenGLDriver * driver,bool useStencil)762 COpenGLFBODepthTexture::COpenGLFBODepthTexture(
763 const core::dimension2d<u32>& size,
764 const io::path& name,
765 COpenGLDriver* driver,
766 bool useStencil)
767 : COpenGLTexture(name, driver), DepthRenderBuffer(0),
768 StencilRenderBuffer(0), UseStencil(useStencil)
769 {
770 #ifdef _DEBUG
771 setDebugName("COpenGLTextureFBO_Depth");
772 #endif
773
774 ImageSize = size;
775 TextureSize = size;
776 InternalFormat = GL_RGBA;
777 PixelFormat = GL_RGBA;
778 PixelType = GL_UNSIGNED_BYTE;
779 HasMipMaps = false;
780
781 if (useStencil)
782 {
783 glGenTextures(1, &DepthRenderBuffer);
784 glBindTexture(GL_TEXTURE_2D, DepthRenderBuffer);
785 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
786 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
787 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
788 #ifdef GL_EXT_packed_depth_stencil
789 if (Driver->queryOpenGLFeature(COpenGLExtensionHandler::IRR_EXT_packed_depth_stencil))
790 {
791 // generate packed depth stencil texture
792 glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_STENCIL_EXT, ImageSize.Width,
793 ImageSize.Height, 0, GL_DEPTH_STENCIL_EXT, GL_UNSIGNED_INT_24_8_EXT, 0);
794 StencilRenderBuffer = DepthRenderBuffer; // stencil is packed with depth
795 }
796 else // generate separate stencil and depth textures
797 #endif
798 {
799 // generate depth texture
800 glTexImage2D(GL_TEXTURE_2D, 0, Driver->getZBufferBits(), ImageSize.Width,
801 ImageSize.Height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
802
803 // generate stencil texture
804 glGenTextures(1, &StencilRenderBuffer);
805 glBindTexture(GL_TEXTURE_2D, StencilRenderBuffer);
806 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
807 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
808 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
809 glTexImage2D(GL_TEXTURE_2D, 0, GL_STENCIL_INDEX, ImageSize.Width,
810 ImageSize.Height, 0, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, 0);
811 }
812 }
813 #ifdef GL_EXT_framebuffer_object
814 else
815 {
816 // generate depth buffer
817 Driver->extGlGenRenderbuffers(1, &DepthRenderBuffer);
818 Driver->extGlBindRenderbuffer(GL_RENDERBUFFER_EXT, DepthRenderBuffer);
819 Driver->extGlRenderbufferStorage(GL_RENDERBUFFER_EXT,
820 Driver->getZBufferBits(), ImageSize.Width,
821 ImageSize.Height);
822 }
823 #endif
824 }
825
826
827 //! destructor
~COpenGLFBODepthTexture()828 COpenGLFBODepthTexture::~COpenGLFBODepthTexture()
829 {
830 if (DepthRenderBuffer && UseStencil)
831 glDeleteTextures(1, &DepthRenderBuffer);
832 else
833 Driver->extGlDeleteRenderbuffers(1, &DepthRenderBuffer);
834 if (StencilRenderBuffer && StencilRenderBuffer != DepthRenderBuffer)
835 glDeleteTextures(1, &StencilRenderBuffer);
836 }
837
838
839 //combine depth texture and rtt
attach(ITexture * renderTex)840 bool COpenGLFBODepthTexture::attach(ITexture* renderTex)
841 {
842 if (!renderTex)
843 return false;
844 video::COpenGLFBOTexture* rtt = static_cast<video::COpenGLFBOTexture*>(renderTex);
845 rtt->bindRTT();
846 #ifdef GL_EXT_framebuffer_object
847 if (UseStencil)
848 {
849 // attach stencil texture to stencil buffer
850 Driver->extGlFramebufferTexture2D(GL_FRAMEBUFFER_EXT,
851 GL_STENCIL_ATTACHMENT_EXT,
852 GL_TEXTURE_2D,
853 StencilRenderBuffer,
854 0);
855
856 // attach depth texture to depth buffer
857 Driver->extGlFramebufferTexture2D(GL_FRAMEBUFFER_EXT,
858 GL_DEPTH_ATTACHMENT_EXT,
859 GL_TEXTURE_2D,
860 DepthRenderBuffer,
861 0);
862 }
863 else
864 {
865 // attach depth renderbuffer to depth buffer
866 Driver->extGlFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT,
867 GL_DEPTH_ATTACHMENT_EXT,
868 GL_RENDERBUFFER_EXT,
869 DepthRenderBuffer);
870 }
871 #endif
872 // check the status
873 if (!checkFBOStatus(Driver))
874 {
875 os::Printer::log("FBO incomplete");
876 return false;
877 }
878 rtt->DepthTexture=this;
879 grab(); // grab the depth buffer, not the RTT
880 rtt->unbindRTT();
881 return true;
882 }
883
884
885 //! Bind Render Target Texture
bindRTT()886 void COpenGLFBODepthTexture::bindRTT()
887 {
888 }
889
890
891 //! Unbind Render Target Texture
unbindRTT()892 void COpenGLFBODepthTexture::unbindRTT()
893 {
894 }
895
896
checkFBOStatus(COpenGLDriver * Driver)897 bool checkFBOStatus(COpenGLDriver* Driver)
898 {
899 #ifdef GL_EXT_framebuffer_object
900 GLenum status = Driver->extGlCheckFramebufferStatus(GL_FRAMEBUFFER_EXT);
901
902 switch (status)
903 {
904 //Our FBO is perfect, return true
905 case GL_FRAMEBUFFER_COMPLETE_EXT:
906 return true;
907
908 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
909 os::Printer::log("FBO has invalid read buffer", ELL_ERROR);
910 break;
911
912 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
913 os::Printer::log("FBO has invalid draw buffer", ELL_ERROR);
914 break;
915
916 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
917 os::Printer::log("FBO has one or several incomplete image attachments", ELL_ERROR);
918 break;
919
920 case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
921 os::Printer::log("FBO has one or several image attachments with different internal formats", ELL_ERROR);
922 break;
923
924 case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
925 os::Printer::log("FBO has one or several image attachments with different dimensions", ELL_ERROR);
926 break;
927
928 // not part of fbo_object anymore, but won't harm as it is just a return value
929 #ifdef GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT
930 case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:
931 os::Printer::log("FBO has a duplicate image attachment", ELL_ERROR);
932 break;
933 #endif
934
935 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
936 os::Printer::log("FBO missing an image attachment", ELL_ERROR);
937 break;
938
939 #ifdef GL_EXT_framebuffer_multisample
940 case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
941 os::Printer::log("FBO wrong multisample setup", ELL_ERROR);
942 break;
943 #endif
944
945 case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
946 os::Printer::log("FBO format unsupported", ELL_ERROR);
947 break;
948
949 default:
950 break;
951 }
952 #endif
953 os::Printer::log("FBO error", ELL_ERROR);
954 // _IRR_DEBUG_BREAK_IF(true);
955 return false;
956 }
957
958
959 } // end namespace video
960 } // end namespace irr
961
962 #endif // _IRR_COMPILE_WITH_OPENGL_
963
964