From 959831f4ef5a3e797f576c3de08cd65032c997ad Mon Sep 17 00:00:00 2001 From: David Walter Seikel Date: Sun, 13 Jan 2013 18:54:10 +1000 Subject: Remove damned ancient DOS line endings from Irrlicht. Hopefully I did not go overboard. --- .../source/Irrlicht/COpenGLTexture.cpp | 1926 ++++++++++---------- 1 file changed, 963 insertions(+), 963 deletions(-) (limited to 'libraries/irrlicht-1.8/source/Irrlicht/COpenGLTexture.cpp') diff --git a/libraries/irrlicht-1.8/source/Irrlicht/COpenGLTexture.cpp b/libraries/irrlicht-1.8/source/Irrlicht/COpenGLTexture.cpp index 3cc41c0..b259071 100644 --- a/libraries/irrlicht-1.8/source/Irrlicht/COpenGLTexture.cpp +++ b/libraries/irrlicht-1.8/source/Irrlicht/COpenGLTexture.cpp @@ -1,963 +1,963 @@ -// Copyright (C) 2002-2012 Nikolaus Gebhardt -// This file is part of the "Irrlicht Engine". -// For conditions of distribution and use, see copyright notice in irrlicht.h - -#include "IrrCompileConfig.h" - -#ifdef _IRR_COMPILE_WITH_OPENGL_ - -#include "irrTypes.h" -#include "COpenGLTexture.h" -#include "COpenGLDriver.h" -#include "os.h" -#include "CColorConverter.h" - -#include "irrString.h" - -namespace irr -{ -namespace video -{ - -//! constructor for usual textures -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), MipmapLegacyMode(true), - 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 = Driver->createImage(ColorFormat, ImageSize); - origImage->copyTo(Image); - } - else - { - Image = Driver->createImage(ColorFormat, TextureSize); - // scale texture - origImage->copyToScaling(Image); - } - uploadTexture(true, mipmapData); - if (!KeepImage) - { - Image->drop(); - Image=0; - } -} - - -//! constructor for basic setup (only for derived classes) -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), - MipmapLegacyMode(true), IsRenderTarget(false), AutomaticMipmapUpdate(false), - ReadOnlyLock(false), KeepImage(true) -{ - #ifdef _DEBUG - setDebugName("COpenGLTexture"); - #endif -} - - -//! destructor -COpenGLTexture::~COpenGLTexture() -{ - if (TextureName) - glDeleteTextures(1, &TextureName); - if (Image) - Image->drop(); -} - - -//! Choose best matching color format, based on texture creation flags -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; -} - - -//! Get opengl values for the GPU texture storage -GLint COpenGLTexture::getOpenGLFormatAndParametersFromColorFormat(ECOLOR_FORMAT format, - GLint& filtering, - GLenum& colorformat, - GLenum& type) -{ - // default - filtering = GL_LINEAR; - colorformat = GL_RGBA; - type = GL_UNSIGNED_BYTE; - GLenum internalformat = GL_RGBA; - - switch(format) - { - case ECF_A1R5G5B5: - colorformat=GL_BGRA_EXT; - type=GL_UNSIGNED_SHORT_1_5_5_5_REV; - internalformat = GL_RGBA; - break; - case ECF_R5G6B5: - colorformat=GL_RGB; - type=GL_UNSIGNED_SHORT_5_6_5; - internalformat = GL_RGB; - break; - case ECF_R8G8B8: - colorformat=GL_BGR; - type=GL_UNSIGNED_BYTE; - internalformat = GL_RGB; - break; - case ECF_A8R8G8B8: - colorformat=GL_BGRA_EXT; - if (Driver->Version > 101) - type=GL_UNSIGNED_INT_8_8_8_8_REV; - internalformat = GL_RGBA; - break; - // Floating Point texture formats. Thanks to Patryk "Nadro" Nadrowski. - case ECF_R16F: - { -#ifdef GL_ARB_texture_rg - filtering = GL_NEAREST; - colorformat = GL_RED; - type = GL_FLOAT; - - internalformat = GL_R16F; -#else - ColorFormat = ECF_A8R8G8B8; - internalformat = GL_RGB8; -#endif - } - break; - case ECF_G16R16F: - { -#ifdef GL_ARB_texture_rg - filtering = GL_NEAREST; - colorformat = GL_RG; - type = GL_FLOAT; - - internalformat = GL_RG16F; -#else - ColorFormat = ECF_A8R8G8B8; - internalformat = GL_RGB8; -#endif - } - break; - case ECF_A16B16G16R16F: - { -#ifdef GL_ARB_texture_rg - filtering = GL_NEAREST; - colorformat = GL_RGBA; - type = GL_FLOAT; - - internalformat = GL_RGBA16F_ARB; -#else - ColorFormat = ECF_A8R8G8B8; - internalformat = GL_RGBA8; -#endif - } - break; - case ECF_R32F: - { -#ifdef GL_ARB_texture_rg - filtering = GL_NEAREST; - colorformat = GL_RED; - type = GL_FLOAT; - - internalformat = GL_R32F; -#else - ColorFormat = ECF_A8R8G8B8; - internalformat = GL_RGB8; -#endif - } - break; - case ECF_G32R32F: - { -#ifdef GL_ARB_texture_rg - filtering = GL_NEAREST; - colorformat = GL_RG; - type = GL_FLOAT; - - internalformat = GL_RG32F; -#else - ColorFormat = ECF_A8R8G8B8; - internalformat = GL_RGB8; -#endif - } - break; - case ECF_A32B32G32R32F: - { -#ifdef GL_ARB_texture_float - filtering = GL_NEAREST; - colorformat = GL_RGBA; - type = GL_FLOAT; - - internalformat = GL_RGBA32F_ARB; -#else - ColorFormat = ECF_A8R8G8B8; - internalformat = GL_RGBA8; -#endif - } - break; - default: - { - os::Printer::log("Unsupported texture format", ELL_ERROR); - internalformat = GL_RGBA8; - } - } -#if defined(GL_ARB_framebuffer_sRGB) || defined(GL_EXT_framebuffer_sRGB) - if (Driver->Params.HandleSRGB) - { - if (internalformat==GL_RGBA) - internalformat=GL_SRGB_ALPHA_EXT; - else if (internalformat==GL_RGB) - internalformat=GL_SRGB_EXT; - } -#endif - return internalformat; -} - - -// prepare values ImageSize, TextureSize, and ColorFormat based on image -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()); -} - - -//! copies the the texture into an open gl texture. -void COpenGLTexture::uploadTexture(bool newTexture, void* mipmapData, u32 level) -{ - // check which image needs to be uploaded - IImage* image = level?MipImage:Image; - if (!image) - { - os::Printer::log("No image for OpenGL texture to upload", ELL_ERROR); - return; - } - - // get correct opengl color data values - GLenum oldInternalFormat = InternalFormat; - GLint filtering; - InternalFormat = getOpenGLFormatAndParametersFromColorFormat(ColorFormat, filtering, PixelFormat, PixelType); - // make sure we don't change the internal format of existing images - if (!newTexture) - InternalFormat=oldInternalFormat; - - Driver->setActiveTexture(0, this); - if (Driver->testGLError()) - os::Printer::log("Could not bind Texture", ELL_ERROR); - - // mipmap handling for main texture - if (!level && newTexture) - { -#ifndef DISABLE_MIPMAPPING -#ifdef GL_SGIS_generate_mipmap - // auto generate if possible and no mipmap data is given - 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); - - AutomaticMipmapUpdate=true; - - if (!Driver->queryFeature(EVDF_FRAMEBUFFER_OBJECT)) - { - glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE ); - MipmapLegacyMode=true; - } - else - MipmapLegacyMode=false; - } - else -#endif - { - // Either generate manually due to missing capability - // or use predefined mipmap data - AutomaticMipmapUpdate=false; - regenerateMipMapLevels(mipmapData); - } - if (HasMipMaps) // might have changed in regenerateMipMapLevels - { - // enable bilinear mipmap filter - 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 - { - // enable bilinear filter without mipmaps - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } - } - - // now get image data and upload to GPU - 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 (!MipmapLegacyMode && AutomaticMipmapUpdate) - { - glEnable(GL_TEXTURE_2D); - Driver->extGlGenerateMipmap(GL_TEXTURE_2D); - } - - if (Driver->testGLError()) - os::Printer::log("Could not glTexImage2D", ELL_ERROR); -} - - -//! lock function -void* COpenGLTexture::lock(E_TEXTURE_LOCK_MODE mode, u32 mipmapLevel) -{ - // store info about which image is locked - IImage* image = (mipmapLevel==0)?Image:MipImage; - ReadOnlyLock |= (mode==ETLM_READ_ONLY); - MipLevelStored = mipmapLevel; - if (!ReadOnlyLock && mipmapLevel) - { -#ifdef GL_SGIS_generate_mipmap - if (Driver->queryFeature(EVDF_MIP_MAP_AUTO_UPDATE)) - { - // do not automatically generate and update mipmaps - glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE); - } -#endif - AutomaticMipmapUpdate=false; - } - - // if data not available or might have changed on GPU download it - if (!image || IsRenderTarget) - { - // prepare the data storage if necessary - 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 = Driver->createImage(ECF_A8R8G8B8, core::dimension2du(width,height)); - } - else - Image = image = Driver->createImage(ECF_A8R8G8B8, ImageSize); - ColorFormat = ECF_A8R8G8B8; - } - if (!image) - return 0; - - if (mode != ETLM_WRITE_ONLY) - { - u8* pixels = static_cast(image->lock()); - if (!pixels) - return 0; - - // we need to keep the correct texture bound later on - GLint tmpTexture; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &tmpTexture); - glBindTexture(GL_TEXTURE_2D, TextureName); - - // we need to flip textures vertical - // however, it seems that this does not hold for mipmap - // textures, for unknown reasons. - - // allows to read pixels in top-to-bottom order -#ifdef GL_MESA_pack_invert - if (!mipmapLevel && Driver->queryOpenGLFeature(COpenGLExtensionHandler::IRR_MESA_pack_invert)) - glPixelStorei(GL_PACK_INVERT_MESA, GL_TRUE); -#endif - - // download GPU data as ARGB8 to pixels; - glGetTexImage(GL_TEXTURE_2D, mipmapLevel, GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels); - - if (!mipmapLevel) - { -#ifdef GL_MESA_pack_invert - if (Driver->queryOpenGLFeature(COpenGLExtensionHandler::IRR_MESA_pack_invert)) - glPixelStorei(GL_PACK_INVERT_MESA, GL_FALSE); - else -#endif - { - // opengl images are horizontally flipped, so we have to fix that here. - 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(); - - //reset old bound texture - glBindTexture(GL_TEXTURE_2D, tmpTexture); - } - } - return image->lock(); -} - - -//! unlock function -void COpenGLTexture::unlock() -{ - // test if miplevel or main texture was locked - IImage* image = MipImage?MipImage:Image; - if (!image) - return; - // unlock image to see changes - image->unlock(); - // copy texture data to GPU - if (!ReadOnlyLock) - uploadTexture(false, 0, MipLevelStored); - ReadOnlyLock = false; - // cleanup local image - if (MipImage) - { - MipImage->drop(); - MipImage=0; - } - else if (!KeepImage) - { - Image->drop(); - Image=0; - } - // update information - if (Image) - ColorFormat=Image->getColorFormat(); - else - ColorFormat=ECF_A8R8G8B8; -} - - -//! Returns size of the original image. -const core::dimension2d& COpenGLTexture::getOriginalSize() const -{ - return ImageSize; -} - - -//! Returns size of the texture. -const core::dimension2d& COpenGLTexture::getSize() const -{ - return TextureSize; -} - - -//! returns driver type of texture, i.e. the driver, which created the texture -E_DRIVER_TYPE COpenGLTexture::getDriverType() const -{ - return EDT_OPENGL; -} - - -//! returns color format of texture -ECOLOR_FORMAT COpenGLTexture::getColorFormat() const -{ - return ColorFormat; -} - - -//! returns pitch of texture (in bytes) -u32 COpenGLTexture::getPitch() const -{ - if (Image) - return Image->getPitch(); - else - return 0; -} - - -//! return open gl texture name -GLuint COpenGLTexture::getOpenGLTextureName() const -{ - return TextureName; -} - - -//! Returns whether this texture has mipmaps -bool COpenGLTexture::hasMipMaps() const -{ - return HasMipMaps; -} - - -//! Regenerates the mip map levels of the texture. Useful after locking and -//! modifying the texture -void COpenGLTexture::regenerateMipMapLevels(void* mipmapData) -{ - if (AutomaticMipmapUpdate || !HasMipMaps || !Image) - return; - if ((Image->getDimension().Width==1) && (Image->getDimension().Height==1)) - return; - - // Manually create mipmaps or use prepared version - u32 width=Image->getDimension().Width; - u32 height=Image->getDimension().Height; - u32 i=0; - u8* target = static_cast(mipmapData); - do - { - if (width>1) - width>>=1; - if (height>1) - height>>=1; - ++i; - if (!target) - target = new u8[width*height*Image->getBytesPerPixel()]; - // create scaled version if no mipdata available - if (!mipmapData) - Image->copyToScaling(target, width, height, Image->getColorFormat()); - glTexImage2D(GL_TEXTURE_2D, i, InternalFormat, width, height, - 0, PixelFormat, PixelType, target); - // get next prepared mipmap data if available - if (mipmapData) - { - mipmapData = static_cast(mipmapData)+width*height*Image->getBytesPerPixel(); - target = static_cast(mipmapData); - } - } - while (width!=1 || height!=1); - // cleanup - if (!mipmapData) - delete [] target; -} - - -bool COpenGLTexture::isRenderTarget() const -{ - return IsRenderTarget; -} - - -void COpenGLTexture::setIsRenderTarget(bool isTarget) -{ - IsRenderTarget = isTarget; -} - - -bool COpenGLTexture::isFrameBufferObject() const -{ - return false; -} - - -//! Bind Render Target Texture -void COpenGLTexture::bindRTT() -{ -} - - -//! Unbind Render Target Texture -void COpenGLTexture::unbindRTT() -{ - Driver->setActiveTexture(0, this); - - // Copy Our ViewPort To The Texture - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, getSize().Width, getSize().Height); -} - - -/* FBO Textures */ - -// helper function for render to texture -static bool checkFBOStatus(COpenGLDriver* Driver); - -//! RTT ColorFrameBuffer constructor -COpenGLFBOTexture::COpenGLFBOTexture(const core::dimension2d& 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()); - - ColorFormat = format; - - GLint FilteringType; - InternalFormat = getOpenGLFormatAndParametersFromColorFormat(format, FilteringType, PixelFormat, PixelType); - - HasMipMaps = false; - IsRenderTarget = true; - -#ifdef GL_EXT_framebuffer_object - // generate frame buffer - Driver->extGlGenFramebuffers(1, &ColorFrameBuffer); - bindRTT(); - - // generate color texture - 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); -#ifdef _DEBUG - driver->testGLError(); -#endif - - // attach color texture to frame buffer - Driver->extGlFramebufferTexture2D(GL_FRAMEBUFFER_EXT, - GL_COLOR_ATTACHMENT0_EXT, - GL_TEXTURE_2D, - TextureName, - 0); -#ifdef _DEBUG - checkFBOStatus(Driver); -#endif - -#endif - unbindRTT(); -} - - -//! destructor -COpenGLFBOTexture::~COpenGLFBOTexture() -{ - if (DepthTexture) - if (DepthTexture->drop()) - Driver->removeDepthTexture(DepthTexture); - if (ColorFrameBuffer) - Driver->extGlDeleteFramebuffers(1, &ColorFrameBuffer); -} - - -bool COpenGLFBOTexture::isFrameBufferObject() const -{ - return true; -} - - -//! Bind Render Target Texture -void COpenGLFBOTexture::bindRTT() -{ -#ifdef GL_EXT_framebuffer_object - if (ColorFrameBuffer != 0) - Driver->extGlBindFramebuffer(GL_FRAMEBUFFER_EXT, ColorFrameBuffer); - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); -#endif -} - - -//! Unbind Render Target Texture -void COpenGLFBOTexture::unbindRTT() -{ -#ifdef GL_EXT_framebuffer_object - if (ColorFrameBuffer != 0) - Driver->extGlBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); -#endif -} - - -/* FBO Depth Textures */ - -//! RTT DepthBuffer constructor -COpenGLFBODepthTexture::COpenGLFBODepthTexture( - const core::dimension2d& 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)) - { - // generate packed depth stencil texture - 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; // stencil is packed with depth - } - else // generate separate stencil and depth textures -#endif - { - // generate depth texture - glTexImage2D(GL_TEXTURE_2D, 0, Driver->getZBufferBits(), ImageSize.Width, - ImageSize.Height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0); - - // generate stencil texture - 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 - { - // generate depth buffer - Driver->extGlGenRenderbuffers(1, &DepthRenderBuffer); - Driver->extGlBindRenderbuffer(GL_RENDERBUFFER_EXT, DepthRenderBuffer); - Driver->extGlRenderbufferStorage(GL_RENDERBUFFER_EXT, - Driver->getZBufferBits(), ImageSize.Width, - ImageSize.Height); - } -#endif -} - - -//! destructor -COpenGLFBODepthTexture::~COpenGLFBODepthTexture() -{ - if (DepthRenderBuffer && UseStencil) - glDeleteTextures(1, &DepthRenderBuffer); - else - Driver->extGlDeleteRenderbuffers(1, &DepthRenderBuffer); - if (StencilRenderBuffer && StencilRenderBuffer != DepthRenderBuffer) - glDeleteTextures(1, &StencilRenderBuffer); -} - - -//combine depth texture and rtt -bool COpenGLFBODepthTexture::attach(ITexture* renderTex) -{ - if (!renderTex) - return false; - video::COpenGLFBOTexture* rtt = static_cast(renderTex); - rtt->bindRTT(); -#ifdef GL_EXT_framebuffer_object - if (UseStencil) - { - // attach stencil texture to stencil buffer - Driver->extGlFramebufferTexture2D(GL_FRAMEBUFFER_EXT, - GL_STENCIL_ATTACHMENT_EXT, - GL_TEXTURE_2D, - StencilRenderBuffer, - 0); - - // attach depth texture to depth buffer - Driver->extGlFramebufferTexture2D(GL_FRAMEBUFFER_EXT, - GL_DEPTH_ATTACHMENT_EXT, - GL_TEXTURE_2D, - DepthRenderBuffer, - 0); - } - else - { - // attach depth renderbuffer to depth buffer - Driver->extGlFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, - GL_DEPTH_ATTACHMENT_EXT, - GL_RENDERBUFFER_EXT, - DepthRenderBuffer); - } -#endif - // check the status - if (!checkFBOStatus(Driver)) - { - os::Printer::log("FBO incomplete"); - return false; - } - rtt->DepthTexture=this; - grab(); // grab the depth buffer, not the RTT - rtt->unbindRTT(); - return true; -} - - -//! Bind Render Target Texture -void COpenGLFBODepthTexture::bindRTT() -{ -} - - -//! Unbind Render Target Texture -void COpenGLFBODepthTexture::unbindRTT() -{ -} - - -bool checkFBOStatus(COpenGLDriver* Driver) -{ -#ifdef GL_EXT_framebuffer_object - GLenum status = Driver->extGlCheckFramebufferStatus(GL_FRAMEBUFFER_EXT); - - switch (status) - { - //Our FBO is perfect, return true - 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; - -// not part of fbo_object anymore, but won't harm as it is just a return value -#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; -} - - -} // end namespace video -} // end namespace irr - -#endif // _IRR_COMPILE_WITH_OPENGL_ - +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#include "IrrCompileConfig.h" + +#ifdef _IRR_COMPILE_WITH_OPENGL_ + +#include "irrTypes.h" +#include "COpenGLTexture.h" +#include "COpenGLDriver.h" +#include "os.h" +#include "CColorConverter.h" + +#include "irrString.h" + +namespace irr +{ +namespace video +{ + +//! constructor for usual textures +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), MipmapLegacyMode(true), + 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 = Driver->createImage(ColorFormat, ImageSize); + origImage->copyTo(Image); + } + else + { + Image = Driver->createImage(ColorFormat, TextureSize); + // scale texture + origImage->copyToScaling(Image); + } + uploadTexture(true, mipmapData); + if (!KeepImage) + { + Image->drop(); + Image=0; + } +} + + +//! constructor for basic setup (only for derived classes) +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), + MipmapLegacyMode(true), IsRenderTarget(false), AutomaticMipmapUpdate(false), + ReadOnlyLock(false), KeepImage(true) +{ + #ifdef _DEBUG + setDebugName("COpenGLTexture"); + #endif +} + + +//! destructor +COpenGLTexture::~COpenGLTexture() +{ + if (TextureName) + glDeleteTextures(1, &TextureName); + if (Image) + Image->drop(); +} + + +//! Choose best matching color format, based on texture creation flags +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; +} + + +//! Get opengl values for the GPU texture storage +GLint COpenGLTexture::getOpenGLFormatAndParametersFromColorFormat(ECOLOR_FORMAT format, + GLint& filtering, + GLenum& colorformat, + GLenum& type) +{ + // default + filtering = GL_LINEAR; + colorformat = GL_RGBA; + type = GL_UNSIGNED_BYTE; + GLenum internalformat = GL_RGBA; + + switch(format) + { + case ECF_A1R5G5B5: + colorformat=GL_BGRA_EXT; + type=GL_UNSIGNED_SHORT_1_5_5_5_REV; + internalformat = GL_RGBA; + break; + case ECF_R5G6B5: + colorformat=GL_RGB; + type=GL_UNSIGNED_SHORT_5_6_5; + internalformat = GL_RGB; + break; + case ECF_R8G8B8: + colorformat=GL_BGR; + type=GL_UNSIGNED_BYTE; + internalformat = GL_RGB; + break; + case ECF_A8R8G8B8: + colorformat=GL_BGRA_EXT; + if (Driver->Version > 101) + type=GL_UNSIGNED_INT_8_8_8_8_REV; + internalformat = GL_RGBA; + break; + // Floating Point texture formats. Thanks to Patryk "Nadro" Nadrowski. + case ECF_R16F: + { +#ifdef GL_ARB_texture_rg + filtering = GL_NEAREST; + colorformat = GL_RED; + type = GL_FLOAT; + + internalformat = GL_R16F; +#else + ColorFormat = ECF_A8R8G8B8; + internalformat = GL_RGB8; +#endif + } + break; + case ECF_G16R16F: + { +#ifdef GL_ARB_texture_rg + filtering = GL_NEAREST; + colorformat = GL_RG; + type = GL_FLOAT; + + internalformat = GL_RG16F; +#else + ColorFormat = ECF_A8R8G8B8; + internalformat = GL_RGB8; +#endif + } + break; + case ECF_A16B16G16R16F: + { +#ifdef GL_ARB_texture_rg + filtering = GL_NEAREST; + colorformat = GL_RGBA; + type = GL_FLOAT; + + internalformat = GL_RGBA16F_ARB; +#else + ColorFormat = ECF_A8R8G8B8; + internalformat = GL_RGBA8; +#endif + } + break; + case ECF_R32F: + { +#ifdef GL_ARB_texture_rg + filtering = GL_NEAREST; + colorformat = GL_RED; + type = GL_FLOAT; + + internalformat = GL_R32F; +#else + ColorFormat = ECF_A8R8G8B8; + internalformat = GL_RGB8; +#endif + } + break; + case ECF_G32R32F: + { +#ifdef GL_ARB_texture_rg + filtering = GL_NEAREST; + colorformat = GL_RG; + type = GL_FLOAT; + + internalformat = GL_RG32F; +#else + ColorFormat = ECF_A8R8G8B8; + internalformat = GL_RGB8; +#endif + } + break; + case ECF_A32B32G32R32F: + { +#ifdef GL_ARB_texture_float + filtering = GL_NEAREST; + colorformat = GL_RGBA; + type = GL_FLOAT; + + internalformat = GL_RGBA32F_ARB; +#else + ColorFormat = ECF_A8R8G8B8; + internalformat = GL_RGBA8; +#endif + } + break; + default: + { + os::Printer::log("Unsupported texture format", ELL_ERROR); + internalformat = GL_RGBA8; + } + } +#if defined(GL_ARB_framebuffer_sRGB) || defined(GL_EXT_framebuffer_sRGB) + if (Driver->Params.HandleSRGB) + { + if (internalformat==GL_RGBA) + internalformat=GL_SRGB_ALPHA_EXT; + else if (internalformat==GL_RGB) + internalformat=GL_SRGB_EXT; + } +#endif + return internalformat; +} + + +// prepare values ImageSize, TextureSize, and ColorFormat based on image +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()); +} + + +//! copies the the texture into an open gl texture. +void COpenGLTexture::uploadTexture(bool newTexture, void* mipmapData, u32 level) +{ + // check which image needs to be uploaded + IImage* image = level?MipImage:Image; + if (!image) + { + os::Printer::log("No image for OpenGL texture to upload", ELL_ERROR); + return; + } + + // get correct opengl color data values + GLenum oldInternalFormat = InternalFormat; + GLint filtering; + InternalFormat = getOpenGLFormatAndParametersFromColorFormat(ColorFormat, filtering, PixelFormat, PixelType); + // make sure we don't change the internal format of existing images + if (!newTexture) + InternalFormat=oldInternalFormat; + + Driver->setActiveTexture(0, this); + if (Driver->testGLError()) + os::Printer::log("Could not bind Texture", ELL_ERROR); + + // mipmap handling for main texture + if (!level && newTexture) + { +#ifndef DISABLE_MIPMAPPING +#ifdef GL_SGIS_generate_mipmap + // auto generate if possible and no mipmap data is given + 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); + + AutomaticMipmapUpdate=true; + + if (!Driver->queryFeature(EVDF_FRAMEBUFFER_OBJECT)) + { + glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE ); + MipmapLegacyMode=true; + } + else + MipmapLegacyMode=false; + } + else +#endif + { + // Either generate manually due to missing capability + // or use predefined mipmap data + AutomaticMipmapUpdate=false; + regenerateMipMapLevels(mipmapData); + } + if (HasMipMaps) // might have changed in regenerateMipMapLevels + { + // enable bilinear mipmap filter + 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 + { + // enable bilinear filter without mipmaps + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + } + + // now get image data and upload to GPU + 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 (!MipmapLegacyMode && AutomaticMipmapUpdate) + { + glEnable(GL_TEXTURE_2D); + Driver->extGlGenerateMipmap(GL_TEXTURE_2D); + } + + if (Driver->testGLError()) + os::Printer::log("Could not glTexImage2D", ELL_ERROR); +} + + +//! lock function +void* COpenGLTexture::lock(E_TEXTURE_LOCK_MODE mode, u32 mipmapLevel) +{ + // store info about which image is locked + IImage* image = (mipmapLevel==0)?Image:MipImage; + ReadOnlyLock |= (mode==ETLM_READ_ONLY); + MipLevelStored = mipmapLevel; + if (!ReadOnlyLock && mipmapLevel) + { +#ifdef GL_SGIS_generate_mipmap + if (Driver->queryFeature(EVDF_MIP_MAP_AUTO_UPDATE)) + { + // do not automatically generate and update mipmaps + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE); + } +#endif + AutomaticMipmapUpdate=false; + } + + // if data not available or might have changed on GPU download it + if (!image || IsRenderTarget) + { + // prepare the data storage if necessary + 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 = Driver->createImage(ECF_A8R8G8B8, core::dimension2du(width,height)); + } + else + Image = image = Driver->createImage(ECF_A8R8G8B8, ImageSize); + ColorFormat = ECF_A8R8G8B8; + } + if (!image) + return 0; + + if (mode != ETLM_WRITE_ONLY) + { + u8* pixels = static_cast(image->lock()); + if (!pixels) + return 0; + + // we need to keep the correct texture bound later on + GLint tmpTexture; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &tmpTexture); + glBindTexture(GL_TEXTURE_2D, TextureName); + + // we need to flip textures vertical + // however, it seems that this does not hold for mipmap + // textures, for unknown reasons. + + // allows to read pixels in top-to-bottom order +#ifdef GL_MESA_pack_invert + if (!mipmapLevel && Driver->queryOpenGLFeature(COpenGLExtensionHandler::IRR_MESA_pack_invert)) + glPixelStorei(GL_PACK_INVERT_MESA, GL_TRUE); +#endif + + // download GPU data as ARGB8 to pixels; + glGetTexImage(GL_TEXTURE_2D, mipmapLevel, GL_BGRA_EXT, GL_UNSIGNED_BYTE, pixels); + + if (!mipmapLevel) + { +#ifdef GL_MESA_pack_invert + if (Driver->queryOpenGLFeature(COpenGLExtensionHandler::IRR_MESA_pack_invert)) + glPixelStorei(GL_PACK_INVERT_MESA, GL_FALSE); + else +#endif + { + // opengl images are horizontally flipped, so we have to fix that here. + 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(); + + //reset old bound texture + glBindTexture(GL_TEXTURE_2D, tmpTexture); + } + } + return image->lock(); +} + + +//! unlock function +void COpenGLTexture::unlock() +{ + // test if miplevel or main texture was locked + IImage* image = MipImage?MipImage:Image; + if (!image) + return; + // unlock image to see changes + image->unlock(); + // copy texture data to GPU + if (!ReadOnlyLock) + uploadTexture(false, 0, MipLevelStored); + ReadOnlyLock = false; + // cleanup local image + if (MipImage) + { + MipImage->drop(); + MipImage=0; + } + else if (!KeepImage) + { + Image->drop(); + Image=0; + } + // update information + if (Image) + ColorFormat=Image->getColorFormat(); + else + ColorFormat=ECF_A8R8G8B8; +} + + +//! Returns size of the original image. +const core::dimension2d& COpenGLTexture::getOriginalSize() const +{ + return ImageSize; +} + + +//! Returns size of the texture. +const core::dimension2d& COpenGLTexture::getSize() const +{ + return TextureSize; +} + + +//! returns driver type of texture, i.e. the driver, which created the texture +E_DRIVER_TYPE COpenGLTexture::getDriverType() const +{ + return EDT_OPENGL; +} + + +//! returns color format of texture +ECOLOR_FORMAT COpenGLTexture::getColorFormat() const +{ + return ColorFormat; +} + + +//! returns pitch of texture (in bytes) +u32 COpenGLTexture::getPitch() const +{ + if (Image) + return Image->getPitch(); + else + return 0; +} + + +//! return open gl texture name +GLuint COpenGLTexture::getOpenGLTextureName() const +{ + return TextureName; +} + + +//! Returns whether this texture has mipmaps +bool COpenGLTexture::hasMipMaps() const +{ + return HasMipMaps; +} + + +//! Regenerates the mip map levels of the texture. Useful after locking and +//! modifying the texture +void COpenGLTexture::regenerateMipMapLevels(void* mipmapData) +{ + if (AutomaticMipmapUpdate || !HasMipMaps || !Image) + return; + if ((Image->getDimension().Width==1) && (Image->getDimension().Height==1)) + return; + + // Manually create mipmaps or use prepared version + u32 width=Image->getDimension().Width; + u32 height=Image->getDimension().Height; + u32 i=0; + u8* target = static_cast(mipmapData); + do + { + if (width>1) + width>>=1; + if (height>1) + height>>=1; + ++i; + if (!target) + target = new u8[width*height*Image->getBytesPerPixel()]; + // create scaled version if no mipdata available + if (!mipmapData) + Image->copyToScaling(target, width, height, Image->getColorFormat()); + glTexImage2D(GL_TEXTURE_2D, i, InternalFormat, width, height, + 0, PixelFormat, PixelType, target); + // get next prepared mipmap data if available + if (mipmapData) + { + mipmapData = static_cast(mipmapData)+width*height*Image->getBytesPerPixel(); + target = static_cast(mipmapData); + } + } + while (width!=1 || height!=1); + // cleanup + if (!mipmapData) + delete [] target; +} + + +bool COpenGLTexture::isRenderTarget() const +{ + return IsRenderTarget; +} + + +void COpenGLTexture::setIsRenderTarget(bool isTarget) +{ + IsRenderTarget = isTarget; +} + + +bool COpenGLTexture::isFrameBufferObject() const +{ + return false; +} + + +//! Bind Render Target Texture +void COpenGLTexture::bindRTT() +{ +} + + +//! Unbind Render Target Texture +void COpenGLTexture::unbindRTT() +{ + Driver->setActiveTexture(0, this); + + // Copy Our ViewPort To The Texture + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, getSize().Width, getSize().Height); +} + + +/* FBO Textures */ + +// helper function for render to texture +static bool checkFBOStatus(COpenGLDriver* Driver); + +//! RTT ColorFrameBuffer constructor +COpenGLFBOTexture::COpenGLFBOTexture(const core::dimension2d& 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()); + + ColorFormat = format; + + GLint FilteringType; + InternalFormat = getOpenGLFormatAndParametersFromColorFormat(format, FilteringType, PixelFormat, PixelType); + + HasMipMaps = false; + IsRenderTarget = true; + +#ifdef GL_EXT_framebuffer_object + // generate frame buffer + Driver->extGlGenFramebuffers(1, &ColorFrameBuffer); + bindRTT(); + + // generate color texture + 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); +#ifdef _DEBUG + driver->testGLError(); +#endif + + // attach color texture to frame buffer + Driver->extGlFramebufferTexture2D(GL_FRAMEBUFFER_EXT, + GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, + TextureName, + 0); +#ifdef _DEBUG + checkFBOStatus(Driver); +#endif + +#endif + unbindRTT(); +} + + +//! destructor +COpenGLFBOTexture::~COpenGLFBOTexture() +{ + if (DepthTexture) + if (DepthTexture->drop()) + Driver->removeDepthTexture(DepthTexture); + if (ColorFrameBuffer) + Driver->extGlDeleteFramebuffers(1, &ColorFrameBuffer); +} + + +bool COpenGLFBOTexture::isFrameBufferObject() const +{ + return true; +} + + +//! Bind Render Target Texture +void COpenGLFBOTexture::bindRTT() +{ +#ifdef GL_EXT_framebuffer_object + if (ColorFrameBuffer != 0) + Driver->extGlBindFramebuffer(GL_FRAMEBUFFER_EXT, ColorFrameBuffer); + glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); +#endif +} + + +//! Unbind Render Target Texture +void COpenGLFBOTexture::unbindRTT() +{ +#ifdef GL_EXT_framebuffer_object + if (ColorFrameBuffer != 0) + Driver->extGlBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); +#endif +} + + +/* FBO Depth Textures */ + +//! RTT DepthBuffer constructor +COpenGLFBODepthTexture::COpenGLFBODepthTexture( + const core::dimension2d& 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)) + { + // generate packed depth stencil texture + 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; // stencil is packed with depth + } + else // generate separate stencil and depth textures +#endif + { + // generate depth texture + glTexImage2D(GL_TEXTURE_2D, 0, Driver->getZBufferBits(), ImageSize.Width, + ImageSize.Height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0); + + // generate stencil texture + 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 + { + // generate depth buffer + Driver->extGlGenRenderbuffers(1, &DepthRenderBuffer); + Driver->extGlBindRenderbuffer(GL_RENDERBUFFER_EXT, DepthRenderBuffer); + Driver->extGlRenderbufferStorage(GL_RENDERBUFFER_EXT, + Driver->getZBufferBits(), ImageSize.Width, + ImageSize.Height); + } +#endif +} + + +//! destructor +COpenGLFBODepthTexture::~COpenGLFBODepthTexture() +{ + if (DepthRenderBuffer && UseStencil) + glDeleteTextures(1, &DepthRenderBuffer); + else + Driver->extGlDeleteRenderbuffers(1, &DepthRenderBuffer); + if (StencilRenderBuffer && StencilRenderBuffer != DepthRenderBuffer) + glDeleteTextures(1, &StencilRenderBuffer); +} + + +//combine depth texture and rtt +bool COpenGLFBODepthTexture::attach(ITexture* renderTex) +{ + if (!renderTex) + return false; + video::COpenGLFBOTexture* rtt = static_cast(renderTex); + rtt->bindRTT(); +#ifdef GL_EXT_framebuffer_object + if (UseStencil) + { + // attach stencil texture to stencil buffer + Driver->extGlFramebufferTexture2D(GL_FRAMEBUFFER_EXT, + GL_STENCIL_ATTACHMENT_EXT, + GL_TEXTURE_2D, + StencilRenderBuffer, + 0); + + // attach depth texture to depth buffer + Driver->extGlFramebufferTexture2D(GL_FRAMEBUFFER_EXT, + GL_DEPTH_ATTACHMENT_EXT, + GL_TEXTURE_2D, + DepthRenderBuffer, + 0); + } + else + { + // attach depth renderbuffer to depth buffer + Driver->extGlFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, + GL_DEPTH_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, + DepthRenderBuffer); + } +#endif + // check the status + if (!checkFBOStatus(Driver)) + { + os::Printer::log("FBO incomplete"); + return false; + } + rtt->DepthTexture=this; + grab(); // grab the depth buffer, not the RTT + rtt->unbindRTT(); + return true; +} + + +//! Bind Render Target Texture +void COpenGLFBODepthTexture::bindRTT() +{ +} + + +//! Unbind Render Target Texture +void COpenGLFBODepthTexture::unbindRTT() +{ +} + + +bool checkFBOStatus(COpenGLDriver* Driver) +{ +#ifdef GL_EXT_framebuffer_object + GLenum status = Driver->extGlCheckFramebufferStatus(GL_FRAMEBUFFER_EXT); + + switch (status) + { + //Our FBO is perfect, return true + 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; + +// not part of fbo_object anymore, but won't harm as it is just a return value +#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; +} + + +} // end namespace video +} // end namespace irr + +#endif // _IRR_COMPILE_WITH_OPENGL_ + -- cgit v1.1