#include "IrrCompileConfig.h"
#ifdef _IRR_COMPILE_WITH_OPENGL_
#include "irrTypes.h"
#include "COpenGLTexture.h"
#include "COpenGLDriver.h"
#include "os.h"
#include "CImage.h"
#include "CColorConverter.h"
#include "irrString.h"
namespace irr
{
namespace video
{
COpenGLTexture::COpenGLTexture(IImage* origImage, const io::path& name, void* mipmapData, COpenGLDriver* driver)
: ITexture(name), ColorFormat(ECF_A8R8G8B8), Driver(driver), Image(0), MipImage(0),
TextureName(0), InternalFormat(GL_RGBA), PixelFormat(GL_BGRA_EXT),
PixelType(GL_UNSIGNED_BYTE), MipLevelStored(0),
IsRenderTarget(false), AutomaticMipmapUpdate(false),
ReadOnlyLock(false), KeepImage(true)
{
#ifdef _DEBUG
setDebugName("COpenGLTexture");
#endif
HasMipMaps = Driver->getTextureCreationFlag(ETCF_CREATE_MIP_MAPS);
getImageValues(origImage);
glGenTextures(1, &TextureName);
if (ImageSize==TextureSize)
{
Image = new CImage(ColorFormat, ImageSize);
origImage->copyTo(Image);
}
else
{
Image = new CImage(ColorFormat, TextureSize);
origImage->copyToScaling(Image);
}
uploadTexture(true, mipmapData);
if (!KeepImage)
{
Image->drop();
Image=0;
}
}
COpenGLTexture::COpenGLTexture(const io::path& name, COpenGLDriver* driver)
: ITexture(name), ColorFormat(ECF_A8R8G8B8), Driver(driver), Image(0), MipImage(0),
TextureName(0), InternalFormat(GL_RGBA), PixelFormat(GL_BGRA_EXT),
PixelType(GL_UNSIGNED_BYTE), MipLevelStored(0),
HasMipMaps(true), IsRenderTarget(false), AutomaticMipmapUpdate(false),
ReadOnlyLock(false), KeepImage(true)
{
#ifdef _DEBUG
setDebugName("COpenGLTexture");
#endif
}
COpenGLTexture::~COpenGLTexture()
{
if (TextureName)
glDeleteTextures(1, &TextureName);
if (Image)
Image->drop();
}
ECOLOR_FORMAT COpenGLTexture::getBestColorFormat(ECOLOR_FORMAT format)
{
ECOLOR_FORMAT destFormat = ECF_A8R8G8B8;
switch (format)
{
case ECF_A1R5G5B5:
if (!Driver->getTextureCreationFlag(ETCF_ALWAYS_32_BIT))
destFormat = ECF_A1R5G5B5;
break;
case ECF_R5G6B5:
if (!Driver->getTextureCreationFlag(ETCF_ALWAYS_32_BIT))
destFormat = ECF_A1R5G5B5;
break;
case ECF_A8R8G8B8:
if (Driver->getTextureCreationFlag(ETCF_ALWAYS_16_BIT) ||
Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_SPEED))
destFormat = ECF_A1R5G5B5;
break;
case ECF_R8G8B8:
if (Driver->getTextureCreationFlag(ETCF_ALWAYS_16_BIT) ||
Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_SPEED))
destFormat = ECF_A1R5G5B5;
default:
break;
}
if (Driver->getTextureCreationFlag(ETCF_NO_ALPHA_CHANNEL))
{
switch (destFormat)
{
case ECF_A1R5G5B5:
destFormat = ECF_R5G6B5;
break;
case ECF_A8R8G8B8:
destFormat = ECF_R8G8B8;
break;
default:
break;
}
}
return destFormat;
}
GLint COpenGLTexture::getOpenGLFormatAndParametersFromColorFormat(ECOLOR_FORMAT format,
GLint& filtering,
GLenum& colorformat,
GLenum& type)
{
filtering = GL_LINEAR;
colorformat = GL_RGBA;
type = GL_UNSIGNED_BYTE;
switch(format)
{
case ECF_A1R5G5B5:
colorformat=GL_BGRA_EXT;
type=GL_UNSIGNED_SHORT_1_5_5_5_REV;
return GL_RGBA;
case ECF_R5G6B5:
colorformat=GL_BGR;
type=GL_UNSIGNED_SHORT_5_6_5_REV;
return GL_RGB;
case ECF_R8G8B8:
colorformat=GL_BGR;
type=GL_UNSIGNED_BYTE;
return GL_RGB;
case ECF_A8R8G8B8:
colorformat=GL_BGRA_EXT;
if (Driver->Version > 101)
type=GL_UNSIGNED_INT_8_8_8_8_REV;
return GL_RGBA;
case ECF_R16F:
{
#ifdef GL_ARB_texture_rg
filtering = GL_NEAREST;
colorformat = GL_RED;
type = GL_FLOAT;
return GL_R16F;
#else
return GL_RGB8;
#endif
}
case ECF_G16R16F:
{
#ifdef GL_ARB_texture_rg
filtering = GL_NEAREST;
colorformat = GL_RG;
type = GL_FLOAT;
return GL_RG16F;
#else
return GL_RGB8;
#endif
}
case ECF_A16B16G16R16F:
{
#ifdef GL_ARB_texture_rg
filtering = GL_NEAREST;
colorformat = GL_RGBA;
type = GL_FLOAT;
return GL_RGBA16F_ARB;
#else
return GL_RGBA8;
#endif
}
case ECF_R32F:
{
#ifdef GL_ARB_texture_rg
filtering = GL_NEAREST;
colorformat = GL_RED;
type = GL_FLOAT;
return GL_R32F;
#else
return GL_RGB8;
#endif
}
case ECF_G32R32F:
{
#ifdef GL_ARB_texture_rg
filtering = GL_NEAREST;
colorformat = GL_RG;
type = GL_FLOAT;
return GL_RG32F;
#else
return GL_RGB8;
#endif
}
case ECF_A32B32G32R32F:
{
#ifdef GL_ARB_texture_float
filtering = GL_NEAREST;
colorformat = GL_RGBA;
type = GL_FLOAT;
return GL_RGBA32F_ARB;
#else
return GL_RGBA8;
#endif
}
default:
{
os::Printer::log("Unsupported texture format", ELL_ERROR);
return GL_RGBA8;
}
}
}
void COpenGLTexture::getImageValues(IImage* image)
{
if (!image)
{
os::Printer::log("No image for OpenGL texture.", ELL_ERROR);
return;
}
ImageSize = image->getDimension();
if ( !ImageSize.Width || !ImageSize.Height)
{
os::Printer::log("Invalid size of image for OpenGL Texture.", ELL_ERROR);
return;
}
const f32 ratio = (f32)ImageSize.Width/(f32)ImageSize.Height;
if ((ImageSize.Width>Driver->MaxTextureSize) && (ratio >= 1.0f))
{
ImageSize.Width = Driver->MaxTextureSize;
ImageSize.Height = (u32)(Driver->MaxTextureSize/ratio);
}
else if (ImageSize.Height>Driver->MaxTextureSize)
{
ImageSize.Height = Driver->MaxTextureSize;
ImageSize.Width = (u32)(Driver->MaxTextureSize*ratio);
}
TextureSize=ImageSize.getOptimalSize(!Driver->queryFeature(EVDF_TEXTURE_NPOT));
ColorFormat = getBestColorFormat(image->getColorFormat());
}
void COpenGLTexture::uploadTexture(bool newTexture, void* mipmapData, u32 level)
{
IImage* image = level?MipImage:Image;
if (!image)
{
os::Printer::log("No image for OpenGL texture to upload", ELL_ERROR);
return;
}
GLenum oldInternalFormat = InternalFormat;
GLint filtering;
InternalFormat = getOpenGLFormatAndParametersFromColorFormat(ColorFormat, filtering, PixelFormat, PixelType);
if (!newTexture)
InternalFormat=oldInternalFormat;
Driver->setActiveTexture(0, this);
if (Driver->testGLError())
os::Printer::log("Could not bind Texture", ELL_ERROR);
if (!level && newTexture)
{
#ifndef DISABLE_MIPMAPPING
#ifdef GL_SGIS_generate_mipmap
if (HasMipMaps && !mipmapData && Driver->queryFeature(EVDF_MIP_MAP_AUTO_UPDATE))
{
if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_SPEED))
glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_FASTEST);
else if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_QUALITY))
glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST);
else
glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_DONT_CARE);
glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE );
AutomaticMipmapUpdate=true;
}
else
#endif
{
AutomaticMipmapUpdate=false;
regenerateMipMapLevels(mipmapData);
}
if (HasMipMaps)
{
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
else
#else
HasMipMaps=false;
os::Printer::log("Did not create OpenGL texture mip maps.", ELL_INFORMATION);
#endif
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
}
void* source = image->lock();
if (newTexture)
glTexImage2D(GL_TEXTURE_2D, level, InternalFormat, image->getDimension().Width,
image->getDimension().Height, 0, PixelFormat, PixelType, source);
else
glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, image->getDimension().Width,
image->getDimension().Height, PixelFormat, PixelType, source);
image->unlock();
if (Driver->testGLError())
os::Printer::log("Could not glTexImage2D", ELL_ERROR);
}
void* COpenGLTexture::lock(E_TEXTURE_LOCK_MODE mode, u32 mipmapLevel)
{
IImage* image = (mipmapLevel==0)?Image:MipImage;
ReadOnlyLock |= (mode==ETLM_READ_ONLY);
MipLevelStored = mipmapLevel;
if (!image || IsRenderTarget)
{
if (!image)
{
if (mipmapLevel)
{
u32 i=0;
u32 width = TextureSize.Width;
u32 height = TextureSize.Height;
do
{
if (width>1)
width>>=1;
if (height>1)
height>>=1;
++i;
}
while (i != mipmapLevel);
MipImage = image = new CImage(ECF_A8R8G8B8, core::dimension2du(width,height));
}
else
Image = image = new CImage(ECF_A8R8G8B8, ImageSize);
ColorFormat = ECF_A8R8G8B8;
}
if (!image)
return 0;
if (mode != ETLM_WRITE_ONLY)
{
u8* pixels = static_cast<u8*>(image->lock());
if (!pixels)
return 0;
GLint tmpTexture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &tmpTexture);
glBindTexture(GL_TEXTURE_2D, TextureName);
#ifdef GL_MESA_pack_invert
if (Driver->queryOpenGLFeature(COpenGLExtensionHandler::IRR_MESA_pack_invert))
glPixelStorei(GL_PACK_INVERT_MESA, GL_TRUE);
#endif
glGetTexImage(GL_TEXTURE_2D, mipmapLevel, GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels);
#ifdef GL_MESA_pack_invert
if (Driver->queryOpenGLFeature(COpenGLExtensionHandler::IRR_MESA_pack_invert))
glPixelStorei(GL_PACK_INVERT_MESA, GL_FALSE);
else
#endif
{
const s32 pitch=image->getPitch();
u8* p2 = pixels + (image->getDimension().Height - 1) * pitch;
u8* tmpBuffer = new u8[pitch];
for (u32 i=0; i < image->getDimension().Height; i += 2)
{
memcpy(tmpBuffer, pixels, pitch);
memcpy(pixels, p2, pitch);
memcpy(p2, tmpBuffer, pitch);
pixels += pitch;
p2 -= pitch;
}
delete [] tmpBuffer;
}
image->unlock();
glBindTexture(GL_TEXTURE_2D, tmpTexture);
}
}
return image->lock();
}
void COpenGLTexture::unlock()
{
IImage* image = MipImage?MipImage:Image;
if (!image)
return;
image->unlock();
if (!ReadOnlyLock)
uploadTexture(false, 0, MipLevelStored);
ReadOnlyLock = false;
if (MipImage)
{
MipImage->drop();
MipImage=0;
}
else if (!KeepImage)
{
Image->drop();
Image=0;
}
if (Image)
ColorFormat=Image->getColorFormat();
else
ColorFormat=ECF_A8R8G8B8;
}
const core::dimension2d<u32>& COpenGLTexture::getOriginalSize() const
{
return ImageSize;
}
const core::dimension2d<u32>& COpenGLTexture::getSize() const
{
return TextureSize;
}
E_DRIVER_TYPE COpenGLTexture::getDriverType() const
{
return EDT_OPENGL;
}
ECOLOR_FORMAT COpenGLTexture::getColorFormat() const
{
return ColorFormat;
}
u32 COpenGLTexture::getPitch() const
{
if (Image)
return Image->getPitch();
else
return 0;
}
GLuint COpenGLTexture::getOpenGLTextureName() const
{
return TextureName;
}
bool COpenGLTexture::hasMipMaps() const
{
return HasMipMaps;
}
void COpenGLTexture::regenerateMipMapLevels(void* mipmapData)
{
if (AutomaticMipmapUpdate || !HasMipMaps || !Image)
return;
if ((Image->getDimension().Width==1) && (Image->getDimension().Height==1))
return;
u32 width=Image->getDimension().Width;
u32 height=Image->getDimension().Height;
u32 i=0;
u8* target = static_cast<u8*>(mipmapData);
do
{
if (width>1)
width>>=1;
if (height>1)
height>>=1;
++i;
if (!target)
target = new u8[width*height*Image->getBytesPerPixel()];
if (!mipmapData)
Image->copyToScaling(target, width, height, Image->getColorFormat());
glTexImage2D(GL_TEXTURE_2D, i, InternalFormat, width, height,
0, PixelFormat, PixelType, target);
if (mipmapData)
{
mipmapData = static_cast<u8*>(mipmapData)+width*height*Image->getBytesPerPixel();
target = static_cast<u8*>(mipmapData);
}
}
while (width!=1 || height!=1);
if (!mipmapData)
delete [] target;
}
bool COpenGLTexture::isRenderTarget() const
{
return IsRenderTarget;
}
void COpenGLTexture::setIsRenderTarget(bool isTarget)
{
IsRenderTarget = isTarget;
}
bool COpenGLTexture::isFrameBufferObject() const
{
return false;
}
void COpenGLTexture::bindRTT()
{
}
void COpenGLTexture::unbindRTT()
{
Driver->setActiveTexture(0, this);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, getSize().Width, getSize().Height);
}
static bool checkFBOStatus(COpenGLDriver* Driver);
COpenGLFBOTexture::COpenGLFBOTexture(const core::dimension2d<u32>& size,
const io::path& name, COpenGLDriver* driver,
ECOLOR_FORMAT format)
: COpenGLTexture(name, driver), DepthTexture(0), ColorFrameBuffer(0)
{
#ifdef _DEBUG
setDebugName("COpenGLTexture_FBO");
#endif
ImageSize = size;
TextureSize = size;
if (ECF_UNKNOWN == format)
format = getBestColorFormat(driver->getColorFormat());
GLint FilteringType;
InternalFormat = getOpenGLFormatAndParametersFromColorFormat(format, FilteringType, PixelFormat, PixelType);
HasMipMaps = false;
IsRenderTarget = true;
#ifdef GL_EXT_framebuffer_object
Driver->extGlGenFramebuffers(1, &ColorFrameBuffer);
bindRTT();
glGenTextures(1, &TextureName);
Driver->setActiveTexture(0, this);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, FilteringType);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, InternalFormat, ImageSize.Width,
ImageSize.Height, 0, PixelFormat, PixelType, 0);
Driver->extGlFramebufferTexture2D(GL_FRAMEBUFFER_EXT,
GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D,
TextureName,
0);
#endif
unbindRTT();
}
COpenGLFBOTexture::~COpenGLFBOTexture()
{
if (DepthTexture)
if (DepthTexture->drop())
Driver->removeDepthTexture(DepthTexture);
if (ColorFrameBuffer)
Driver->extGlDeleteFramebuffers(1, &ColorFrameBuffer);
}
bool COpenGLFBOTexture::isFrameBufferObject() const
{
return true;
}
void COpenGLFBOTexture::bindRTT()
{
#ifdef GL_EXT_framebuffer_object
if (ColorFrameBuffer != 0)
Driver->extGlBindFramebuffer(GL_FRAMEBUFFER_EXT, ColorFrameBuffer);
glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
#endif
}
void COpenGLFBOTexture::unbindRTT()
{
#ifdef GL_EXT_framebuffer_object
if (ColorFrameBuffer != 0)
Driver->extGlBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
#endif
}
COpenGLFBODepthTexture::COpenGLFBODepthTexture(
const core::dimension2d<u32>& size,
const io::path& name,
COpenGLDriver* driver,
bool useStencil)
: COpenGLTexture(name, driver), DepthRenderBuffer(0),
StencilRenderBuffer(0), UseStencil(useStencil)
{
#ifdef _DEBUG
setDebugName("COpenGLTextureFBO_Depth");
#endif
ImageSize = size;
TextureSize = size;
InternalFormat = GL_RGBA;
PixelFormat = GL_RGBA;
PixelType = GL_UNSIGNED_BYTE;
HasMipMaps = false;
if (useStencil)
{
glGenTextures(1, &DepthRenderBuffer);
glBindTexture(GL_TEXTURE_2D, DepthRenderBuffer);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
#ifdef GL_EXT_packed_depth_stencil
if (Driver->queryOpenGLFeature(COpenGLExtensionHandler::IRR_EXT_packed_depth_stencil))
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_STENCIL_EXT, ImageSize.Width,
ImageSize.Height, 0, GL_DEPTH_STENCIL_EXT, GL_UNSIGNED_INT_24_8_EXT, 0);
StencilRenderBuffer = DepthRenderBuffer;
}
else
#endif
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, ImageSize.Width,
ImageSize.Height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
glGenTextures(1, &StencilRenderBuffer);
glBindTexture(GL_TEXTURE_2D, StencilRenderBuffer);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_STENCIL_INDEX, ImageSize.Width,
ImageSize.Height, 0, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, 0);
}
}
#ifdef GL_EXT_framebuffer_object
else
{
Driver->extGlGenRenderbuffers(1, &DepthRenderBuffer);
Driver->extGlBindRenderbuffer(GL_RENDERBUFFER_EXT, DepthRenderBuffer);
Driver->extGlRenderbufferStorage(GL_RENDERBUFFER_EXT,
GL_DEPTH_COMPONENT, ImageSize.Width,
ImageSize.Height);
}
#endif
}
COpenGLFBODepthTexture::~COpenGLFBODepthTexture()
{
if (DepthRenderBuffer && UseStencil)
glDeleteTextures(1, &DepthRenderBuffer);
else
Driver->extGlDeleteRenderbuffers(1, &DepthRenderBuffer);
if (StencilRenderBuffer && StencilRenderBuffer != DepthRenderBuffer)
glDeleteTextures(1, &StencilRenderBuffer);
}
bool COpenGLFBODepthTexture::attach(ITexture* renderTex)
{
if (!renderTex)
return false;
video::COpenGLFBOTexture* rtt = static_cast<video::COpenGLFBOTexture*>(renderTex);
rtt->bindRTT();
#ifdef GL_EXT_framebuffer_object
if (UseStencil)
{
Driver->extGlFramebufferTexture2D(GL_FRAMEBUFFER_EXT,
GL_STENCIL_ATTACHMENT_EXT,
GL_TEXTURE_2D,
StencilRenderBuffer,
0);
Driver->extGlFramebufferTexture2D(GL_FRAMEBUFFER_EXT,
GL_DEPTH_ATTACHMENT_EXT,
GL_TEXTURE_2D,
DepthRenderBuffer,
0);
}
else
{
Driver->extGlFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT,
GL_DEPTH_ATTACHMENT_EXT,
GL_RENDERBUFFER_EXT,
DepthRenderBuffer);
}
#endif
if (!checkFBOStatus(Driver))
{
os::Printer::log("FBO incomplete");
return false;
}
rtt->DepthTexture=this;
grab();
rtt->unbindRTT();
return true;
}
void COpenGLFBODepthTexture::bindRTT()
{
}
void COpenGLFBODepthTexture::unbindRTT()
{
}
bool checkFBOStatus(COpenGLDriver* Driver)
{
#ifdef GL_EXT_framebuffer_object
GLenum status = Driver->extGlCheckFramebufferStatus(GL_FRAMEBUFFER_EXT);
switch (status)
{
case GL_FRAMEBUFFER_COMPLETE_EXT:
return true;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
os::Printer::log("FBO has invalid read buffer", ELL_ERROR);
break;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
os::Printer::log("FBO has invalid draw buffer", ELL_ERROR);
break;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
os::Printer::log("FBO has one or several incomplete image attachments", ELL_ERROR);
break;
case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
os::Printer::log("FBO has one or several image attachments with different internal formats", ELL_ERROR);
break;
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
os::Printer::log("FBO has one or several image attachments with different dimensions", ELL_ERROR);
break;
#ifdef GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT
case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT:
os::Printer::log("FBO has a duplicate image attachment", ELL_ERROR);
break;
#endif
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
os::Printer::log("FBO missing an image attachment", ELL_ERROR);
break;
#ifdef GL_EXT_framebuffer_multisample
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
os::Printer::log("FBO wrong multisample setup", ELL_ERROR);
break;
#endif
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
os::Printer::log("FBO format unsupported", ELL_ERROR);
break;
default:
break;
}
#endif
os::Printer::log("FBO error", ELL_ERROR);
_IRR_DEBUG_BREAK_IF(true);
return false;
}
}
}
#endif