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