From 49c11b0690e07ab37270b441476ca9a20f22f0ed Mon Sep 17 00:00:00 2001 From: nadro Date: Tue, 19 Mar 2013 15:54:47 +0000 Subject: [PATCH] - Clean-up OGL ES2 driver (mainly stuff related to textures). git-svn-id: http://svn.code.sf.net/p/irrlicht/code/branches/ogl-es@4481 dfc29bdd-3216-0410-991c-e03cc46cb475 --- examples/08.SpecialFX/project.properties | 28 +- source/Irrlicht/COGLES2Driver.cpp | 117 +- source/Irrlicht/COGLES2Driver.h | 10 + source/Irrlicht/COGLES2ExtensionHandler.cpp | 28 +- source/Irrlicht/COGLES2ExtensionHandler.h | 13 +- source/Irrlicht/COGLES2Texture.cpp | 1298 ++++++++++--------- source/Irrlicht/COGLES2Texture.h | 306 ++--- 7 files changed, 936 insertions(+), 864 deletions(-) diff --git a/examples/08.SpecialFX/project.properties b/examples/08.SpecialFX/project.properties index 7a7c53b1..6f9611b1 100644 --- a/examples/08.SpecialFX/project.properties +++ b/examples/08.SpecialFX/project.properties @@ -1,14 +1,14 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}\tools\proguard\proguard-android.txt:proguard-project.txt - -# Project target. -target=Google Inc.:Google APIs:16 +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}\tools\proguard\proguard-android.txt:proguard-project.txt + +# Project target. +target=android-10 diff --git a/source/Irrlicht/COGLES2Driver.cpp b/source/Irrlicht/COGLES2Driver.cpp index 98797b26..53304a51 100644 --- a/source/Irrlicht/COGLES2Driver.cpp +++ b/source/Irrlicht/COGLES2Driver.cpp @@ -220,7 +220,7 @@ namespace video #endif os::Printer::log("Creating EglContext..."); EglContext = eglCreateContext(EglDisplay, config, EGL_NO_CONTEXT, contextAttrib); - if (testEGLError()) + if (EGL_NO_CONTEXT == EglContext) { os::Printer::log("FAILED\n"); os::Printer::log("Could not create Context for OpenGL-ES2 display."); @@ -349,9 +349,9 @@ namespace video DriverAttributes->setAttribute("MaxTextures", MaxTextureUnits); DriverAttributes->setAttribute("MaxSupportedTextures", MaxSupportedTextures); - DriverAttributes->setAttribute("MaxLights", MaxLights); +// DriverAttributes->setAttribute("MaxLights", MaxLights); DriverAttributes->setAttribute("MaxAnisotropy", MaxAnisotropy); - DriverAttributes->setAttribute("MaxUserClipPlanes", MaxUserClipPlanes); +// DriverAttributes->setAttribute("MaxUserClipPlanes", MaxUserClipPlanes); // DriverAttributes->setAttribute("MaxAuxBuffers", MaxAuxBuffers); // DriverAttributes->setAttribute("MaxMultipleRenderTargets", MaxMultipleRenderTargets); DriverAttributes->setAttribute("MaxIndices", (s32)MaxIndices); @@ -363,7 +363,7 @@ namespace video glPixelStorei(GL_PACK_ALIGNMENT, 1); // Reset The Current Viewport - glViewport(0, 0, screenSize.Width, screenSize.Height); + BridgeCalls->setViewport(core::rect(0, 0, screenSize.Width, screenSize.Height)); UserClipPlane.reallocate(0); @@ -1743,7 +1743,7 @@ namespace video //! returns a device dependent texture from a software surface (IImage) video::ITexture* COGLES2Driver::createDeviceDependentTexture(IImage* surface, const io::path& name, void* mipmapData) { - return new COGLES2Texture(surface, name, this); + return new COGLES2Texture(surface, name, mipmapData, this); } @@ -2204,7 +2204,7 @@ namespace video //! returns the maximal amount of dynamic lights the device can handle u32 COGLES2Driver::getMaximalDynamicLightAmount() const { - return MaxLights; + return 8; } @@ -2228,11 +2228,7 @@ namespace video vp.clipAgainst(rendert); if (vp.getHeight() > 0 && vp.getWidth() > 0) - { - glViewport(vp.UpperLeftCorner.X, - getCurrentRenderTargetSize().Height - vp.UpperLeftCorner.Y - vp.getHeight(), - vp.getWidth(), vp.getHeight()); - } + BridgeCalls->setViewport(core::rect(vp.UpperLeftCorner.X, getCurrentRenderTargetSize().Height - vp.UpperLeftCorner.Y - vp.getHeight(), vp.getWidth(), vp.getHeight())); ViewPort = vp; testGLError(); @@ -2404,7 +2400,7 @@ namespace video void COGLES2Driver::OnResize(const core::dimension2d& size) { CNullDriver::OnResize(size); - glViewport(0, 0, size.Width, size.Height); + BridgeCalls->setViewport(core::rect(0, 0, size.Width, size.Height)); testGLError(); } @@ -2541,42 +2537,28 @@ namespace video video::ITexture* rtt = 0; - // if driver supports FrameBufferObjects, use them - if (queryFeature(EVDF_FRAMEBUFFER_OBJECT)) + rtt = new COGLES2FBOTexture(size, name, this, format); + if (rtt) { - rtt = new COGLES2FBOTexture(size, name, this, format); - if (rtt) - { - bool success = false; - addTexture(rtt); + bool success = false; + addTexture(rtt); - ITexture* tex = createDepthTexture(rtt); - if (tex) - { - success = static_cast(tex)->attach(rtt); - if (!success) - { - removeDepthTexture(tex); - } - tex->drop(); - } - rtt->drop(); + ITexture* tex = createDepthTexture(rtt); + if (tex) + { + success = static_cast(tex)->attach(rtt); if (!success) { - removeTexture(rtt); - rtt=0; + removeDepthTexture(tex); } + tex->drop(); + } + rtt->drop(); + if (!success) + { + removeTexture(rtt); + rtt=0; } - } - else - { - // the simple texture is only possible for size <= screensize - // we try to find an optimal size with the original constraints - core::dimension2du destSize(core::min_(size.Width, ScreenSize.Width), core::min_(size.Height, ScreenSize.Height)); - destSize = destSize.getOptimalSize((size == size.getOptimalSize()), false, false); - rtt = addTexture(destSize, name, ECF_A8R8G8B8); - if (rtt) - static_cast(rtt)->setIsRenderTarget(true); } //restore mip-mapping @@ -2617,13 +2599,14 @@ namespace video if (texture) { // we want to set a new target. so do this. + BridgeCalls->setViewport(core::rect(0, 0, texture->getSize().Width, texture->getSize().Height)); RenderTargetTexture = static_cast(texture); RenderTargetTexture->bindRTT(); CurrentRendertargetSize = texture->getSize(); } else { - glViewport(0, 0, ScreenSize.Width, ScreenSize.Height); + BridgeCalls->setViewport(core::rect(0, 0, ScreenSize.Width, ScreenSize.Height)); RenderTargetTexture = 0; CurrentRendertargetSize = core::dimension2d(0, 0); } @@ -2816,9 +2799,6 @@ namespace video //! Enable/disable a clipping plane. void COGLES2Driver::enableClipPlane(u32 index, bool enable) { - if (index >= MaxUserClipPlanes) - return; - UserClipPlane[index].Enabled = enable; } @@ -2861,6 +2841,38 @@ namespace video return r; } + GLenum COGLES2Driver::getZBufferBits() const + { +/*#if defined(GL_OES_depth24) + if (Driver->queryOpenGLFeature(COGLES2ExtensionHandler::IRR_OES_depth24)) + InternalFormat = GL_DEPTH_COMPONENT24_OES; + else +#endif +#if defined(GL_OES_depth32) + if (Driver->queryOpenGLFeature(COGLES2ExtensionHandler::IRR_OES_depth32)) + InternalFormat = GL_DEPTH_COMPONENT32_OES; + else +#endif*/ + + GLenum bits = GL_DEPTH_COMPONENT16;//0; + /*switch (Params.ZBufferBits) + { + case 16: + bits = GL_DEPTH_COMPONENT16; + break; + case 24: + bits = GL_DEPTH_COMPONENT24; + break; + case 32: + bits = GL_DEPTH_COMPONENT32; + break; + default: + bits = GL_DEPTH_COMPONENT; + break; + }*/ + return bits; + } + const SMaterial& COGLES2Driver::getCurrentMaterial() const { return Material; @@ -2875,7 +2887,7 @@ namespace video BlendSource(GL_ONE), BlendDestination(GL_ZERO), Blend(false), CullFaceMode(GL_BACK), CullFace(false), DepthFunc(GL_LESS), DepthMask(true), DepthTest(false), - Program(0), ActiveTexture(GL_TEXTURE0) + Program(0), ActiveTexture(GL_TEXTURE0), Viewport(core::rect(0, 0, 0, 0)) { // Initial OpenGL values from specification. @@ -3003,13 +3015,22 @@ namespace video setActiveTexture(GL_TEXTURE0 + stage); if(Driver->CurrentTexture[stage]) - glBindTexture(GL_TEXTURE_2D, static_cast(Driver->CurrentTexture[stage])->getOGLES2TextureName()); + glBindTexture(GL_TEXTURE_2D, static_cast(Driver->CurrentTexture[stage])->getOpenGLTextureName()); Texture[stage] = Driver->CurrentTexture[stage]; } } } + void COGLES2CallBridge::setViewport(const core::rect& viewport) + { + if (Viewport != viewport) + { + glViewport(viewport.UpperLeftCorner.X, viewport.UpperLeftCorner.Y, viewport.LowerRightCorner.X, viewport.LowerRightCorner.Y); + Viewport = viewport; + } + } + } // end namespace } // end namespace diff --git a/source/Irrlicht/COGLES2Driver.h b/source/Irrlicht/COGLES2Driver.h index ae54f3d4..bb9ac3b4 100644 --- a/source/Irrlicht/COGLES2Driver.h +++ b/source/Irrlicht/COGLES2Driver.h @@ -58,6 +58,7 @@ namespace video class COGLES2Driver : public CNullDriver, public IMaterialRendererServices, public COGLES2ExtensionHandler { friend class COGLES2CallBridge; + friend class COGLES2Texture; public: #if defined(_IRR_COMPILE_WITH_X11_DEVICE_) || defined(_IRR_COMPILE_WITH_SDL_DEVICE_) || defined(_IRR_WINDOWS_API_) || defined(_IRR_COMPILE_WITH_CONSOLE_DEVICE_) @@ -361,6 +362,9 @@ namespace video //! Convert E_BLEND_FACTOR to OpenGL equivalent GLenum getGLBlend(E_BLEND_FACTOR factor) const; + //! Get ZBuffer bits. + GLenum getZBufferBits() const; + //! Get current material. const SMaterial& getCurrentMaterial() const; @@ -503,6 +507,10 @@ namespace video void setActiveTexture(GLenum texture); void setTexture(u32 stage); + + // Viewport calls. + + void setViewport(const core::rect& viewport); private: COGLES2Driver* Driver; @@ -523,6 +531,8 @@ namespace video GLenum ActiveTexture; const ITexture* Texture[MATERIAL_MAX_TEXTURES]; + + core::rect Viewport; }; } // end namespace video diff --git a/source/Irrlicht/COGLES2ExtensionHandler.cpp b/source/Irrlicht/COGLES2ExtensionHandler.cpp index 29cc71be..41cf46a3 100644 --- a/source/Irrlicht/COGLES2ExtensionHandler.cpp +++ b/source/Irrlicht/COGLES2ExtensionHandler.cpp @@ -160,9 +160,9 @@ namespace video COGLES2ExtensionHandler::COGLES2ExtensionHandler() : EGLVersion(0), Version(0), MaxTextureUnits(0), MaxSupportedTextures(0), - MaxLights(0), MaxAnisotropy(1), MaxUserClipPlanes(6), MaxTextureSize(1), - MaxIndices(0xffff), MaxTextureLODBias(0.f), MultiTextureExtension(false), - MultiSamplingExtension(false), StencilBuffer(false) + MaxAnisotropy(1), MaxTextureSize(1), + MaxIndices(0xffff), MaxTextureLODBias(0.f), + StencilBuffer(false) { for (u32 i=0; i(val)); - MultiTextureExtension = true; - //TODO : OpenGL ES 2.0 Port - //glGetIntegerv(GL_MAX_LIGHTS, &val); - MaxLights = 8; -#ifdef GL_EXT_texture_filter_anisotropic + + #ifdef GL_EXT_texture_filter_anisotropic if (FeatureAvailable[IRR_EXT_texture_filter_anisotropic]) { glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &val); MaxAnisotropy = static_cast(val); } -#endif + #endif + #ifdef GL_MAX_ELEMENTS_INDICES + glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &val); + MaxIndices=val; + #endif glGetIntegerv(GL_MAX_TEXTURE_SIZE, &val); MaxTextureSize=static_cast(val); -#ifdef GL_EXT_texture_lod_bias + #ifdef GL_EXT_texture_lod_bias if (FeatureAvailable[IRR_EXT_texture_lod_bias]) glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS_EXT, &MaxTextureLODBias); -#endif + #endif + glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, DimAliasedLine); + glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, DimAliasedPoint); + MaxTextureUnits = core::min_(MaxSupportedTextures, static_cast(MATERIAL_MAX_TEXTURES)); } diff --git a/source/Irrlicht/COGLES2ExtensionHandler.h b/source/Irrlicht/COGLES2ExtensionHandler.h index 08bf09aa..8f9ad36d 100644 --- a/source/Irrlicht/COGLES2ExtensionHandler.h +++ b/source/Irrlicht/COGLES2ExtensionHandler.h @@ -190,9 +190,7 @@ namespace video { case EVDF_RENDER_TO_TARGET: case EVDF_HARDWARE_TL: - return true; case EVDF_MULTITEXTURE: - return MultiTextureExtension; case EVDF_BILINEAR_FILTER: case EVDF_MIP_MAP: case EVDF_MIP_MAP_AUTO_UPDATE: @@ -235,15 +233,14 @@ namespace video u16 Version; u8 MaxTextureUnits; u8 MaxSupportedTextures; - u8 MaxLights; u8 MaxAnisotropy; - u8 MaxUserClipPlanes; - u32 MaxTextureSize; u32 MaxIndices; + u32 MaxTextureSize; f32 MaxTextureLODBias; - - bool MultiTextureExtension; - bool MultiSamplingExtension; + //! Minimal and maximal supported thickness for lines without smoothing + GLfloat DimAliasedLine[2]; + //! Minimal and maximal supported thickness for points without smoothing + GLfloat DimAliasedPoint[2]; bool StencilBuffer; bool FeatureAvailable[IRR_OGLES2_Feature_Count]; }; diff --git a/source/Irrlicht/COGLES2Texture.cpp b/source/Irrlicht/COGLES2Texture.cpp index bf81e7c6..fcbddf9e 100644 --- a/source/Irrlicht/COGLES2Texture.cpp +++ b/source/Irrlicht/COGLES2Texture.cpp @@ -15,6 +15,7 @@ #include "os.h" #include "CImage.h" #include "CColorConverter.h" + #include "irrString.h" #if !defined(_IRR_COMPILE_WITH_IPHONE_DEVICE_) @@ -27,7 +28,7 @@ namespace { #ifndef GL_BGRA // we need to do this for the IMG_BGRA8888 extension -int GL_BGRA = GL_RGBA; +int GL_BGRA=GL_RGBA; #endif } @@ -36,714 +37,737 @@ namespace irr namespace video { - //! constructor for usual textures - COGLES2Texture::COGLES2Texture( IImage* origImage, const io::path& name, COGLES2Driver* driver ) - : ITexture( name ), Driver( driver ), Image( 0 ), - TextureName( 0 ), InternalFormat( GL_RGBA ), - PixelFormat(GL_BGRA), PixelType( GL_UNSIGNED_BYTE ), - HasMipMaps( true ), IsRenderTarget( false ), AutomaticMipmapUpdate( false ), - UseStencil( false ), ReadOnlyLock( false ) +//! constructor for usual textures +COGLES2Texture::COGLES2Texture(IImage* origImage, const io::path& name, void* mipmapData, COGLES2Driver* 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("COGLES2Texture"); + #endif + + HasMipMaps = Driver->getTextureCreationFlag(ETCF_CREATE_MIP_MAPS); + getImageValues(origImage); + + glGenTextures(1, &TextureName); + + if (ImageSize==TextureSize) { -#ifdef _DEBUG - setDebugName( "COGLES2Texture" ); -#endif - - HasMipMaps = Driver->getTextureCreationFlag( ETCF_CREATE_MIP_MAPS ); - getImageData( origImage ); - - if ( Image ) - { - glGenTextures( 1, &TextureName ); - copyTexture(); - } + Image = Driver->createImage(ColorFormat, ImageSize); + origImage->copyTo(Image); } - - - //! constructor for basic setup (only for derived classes) - COGLES2Texture::COGLES2Texture( const io::path& name, COGLES2Driver* driver ) - : ITexture( name ), Driver( driver ), Image( 0 ), - TextureName( 0 ), InternalFormat( GL_RGBA ), PixelFormat( GL_RGBA ), - PixelType( GL_UNSIGNED_BYTE ), - HasMipMaps( true ), IsRenderTarget( false ), AutomaticMipmapUpdate( false ), - ReadOnlyLock( false ) + else { -#ifdef _DEBUG - setDebugName( "COGLES2Texture" ); -#endif + Image = Driver->createImage(ColorFormat, TextureSize); + // scale texture + origImage->copyToScaling(Image); } - - - //! destructor - COGLES2Texture::~COGLES2Texture() + uploadTexture(true, mipmapData); + if (!KeepImage) { - glDeleteTextures( 1, &TextureName ); - if ( Image ) - Image->drop(); + Image->drop(); + Image=0; } +} - ECOLOR_FORMAT COGLES2Texture::getBestColorFormat( ECOLOR_FORMAT format ) +//! constructor for basic setup (only for derived classes) +COGLES2Texture::COGLES2Texture(const io::path& name, COGLES2Driver* 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("COGLES2Texture"); + #endif +} + + +//! destructor +COGLES2Texture::~COGLES2Texture() +{ + if (TextureName) + glDeleteTextures(1, &TextureName); + if (Image) + Image->drop(); +} + + +//! Choose best matching color format, based on texture creation flags +ECOLOR_FORMAT COGLES2Texture::getBestColorFormat(ECOLOR_FORMAT format) +{ + ECOLOR_FORMAT destFormat = ECF_A8R8G8B8; + switch (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: - 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; + destFormat = ECF_R5G6B5; + 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; - break; + destFormat = ECF_R8G8B8; + break; default: - destFormat = ECF_A8R8G8B8; - break; + 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; + } + return destFormat; +} + + +// prepare values ImageSize, TextureSize, and ColorFormat based on image +void COGLES2Texture::getImageValues(IImage* image) +{ + if (!image) + { + os::Printer::log("No image for OpenGL texture.", ELL_ERROR); + return; } + ImageSize = image->getDimension(); - void COGLES2Texture::getImageData( IImage* image ) + if ( !ImageSize.Width || !ImageSize.Height) { - if ( !image ) - { - os::Printer::log( "No image for OGLES2 texture.", ELL_ERROR ); - return; - } - - ImageSize = image->getDimension(); - - if ( !ImageSize.Width || !ImageSize.Height ) - { - os::Printer::log( "Invalid size of image for OGLES2 Texture.", ELL_ERROR ); - return; - } - - const core::dimension2d nImageSize = ImageSize.getOptimalSize( !Driver->queryFeature( EVDF_TEXTURE_NPOT ) ); - const ECOLOR_FORMAT destFormat = getBestColorFormat( image->getColorFormat() ); - - Image = new CImage( destFormat, nImageSize ); - // copy texture - image->copyToScaling( Image ); + os::Printer::log("Invalid size of image for OpenGL Texture.", ELL_ERROR); + return; } - - //! copies the texture into an opengl-es2 texture. - void COGLES2Texture::copyTexture( bool newTexture ) + 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(false); + + ColorFormat = getBestColorFormat(image->getColorFormat()); +} + + +//! copies the the texture into an open gl texture. +void COGLES2Texture::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 OGLES2 texture to upload", ELL_ERROR); + return; + } + #ifndef GL_BGRA - // whoa, pretty badly implemented extension... - if (Driver->queryOpenGLFeature(COGLES2ExtensionHandler::IRR_IMG_texture_format_BGRA8888) || - Driver->queryOpenGLFeature(COGLES2ExtensionHandler::IRR_EXT_texture_format_BGRA8888)) - GL_BGRA = 0x80E1; - else - GL_BGRA = GL_RGBA; + // whoa, pretty badly implemented extension... + if (Driver->FeatureAvailable[COGLES2ExtensionHandler::IRR_IMG_texture_format_BGRA8888] || Driver->FeatureAvailable[COGLES2ExtensionHandler::IRR_EXT_texture_format_BGRA8888]) + GL_BGRA=0x80E1; + else + GL_BGRA=GL_RGBA; #endif - if ( !Image ) + + GLenum oldInternalFormat = InternalFormat; + void(*convert)(const void*, s32, void*)=0; + switch (Image->getColorFormat()) + { + case ECF_A1R5G5B5: + InternalFormat=GL_RGBA; + PixelFormat=GL_RGBA; + PixelType=GL_UNSIGNED_SHORT_5_5_5_1; + convert=CColorConverter::convert_A1R5G5B5toR5G5B5A1; + break; + case ECF_R5G6B5: + InternalFormat=GL_RGB; + PixelFormat=GL_RGB; + PixelType=GL_UNSIGNED_SHORT_5_6_5; + break; + case ECF_R8G8B8: + InternalFormat=GL_RGB; + PixelFormat=GL_RGB; + PixelType=GL_UNSIGNED_BYTE; + convert=CColorConverter::convert_R8G8B8toB8G8R8; + break; + case ECF_A8R8G8B8: + PixelType=GL_UNSIGNED_BYTE; + if (!Driver->queryOpenGLFeature(COGLES2ExtensionHandler::IRR_IMG_texture_format_BGRA8888) && !Driver->queryOpenGLFeature(COGLES2ExtensionHandler::IRR_EXT_texture_format_BGRA8888)) + { + convert=CColorConverter::convert_A8R8G8B8toA8B8G8R8; + InternalFormat=GL_RGBA; + PixelFormat=GL_RGBA; + } + else + { + InternalFormat=GL_BGRA; + PixelFormat=GL_BGRA; + } + break; + default: + os::Printer::log("Unsupported texture format", ELL_ERROR); + break; + } + // Hack for iPhone SDK, which requires a different InternalFormat +#ifdef _IRR_IPHONE_PLATFORM_ + if (InternalFormat==GL_BGRA) + InternalFormat=GL_RGBA; +#endif + // make sure we don't change the internal format of existing matrices + if (!newTexture) + InternalFormat=oldInternalFormat; + + Driver->setActiveTexture(0, this); + Driver->getBridgeCalls()->setTexture(0); + + if (Driver->testGLError()) + os::Printer::log("Could not bind Texture", ELL_ERROR); + + // mipmap handling for main texture + if (!level && newTexture) + { +#ifndef DISABLE_MIPMAPPING + // auto generate if possible and no mipmap data is given + if (HasMipMaps && !mipmapData) { - os::Printer::log( "No image for OGLES2 texture to upload", ELL_ERROR ); - return; + if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_SPEED)) + glHint(GL_GENERATE_MIPMAP_HINT, GL_FASTEST); + else if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_QUALITY)) + glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST); + else + glHint(GL_GENERATE_MIPMAP_HINT, GL_DONT_CARE); + + AutomaticMipmapUpdate=true; + } + else + { + // Either generate manually due to missing capability + // or use predefined mipmap data + AutomaticMipmapUpdate=false; + regenerateMipMapLevels(mipmapData); } - void( *convert )( const void*, s32, void* ) = 0; - switch ( Image->getColorFormat() ) + if (HasMipMaps) // might have changed in regenerateMipMapLevels { - case ECF_A1R5G5B5: - InternalFormat = GL_RGBA; - PixelFormat = GL_RGBA; - PixelType = GL_UNSIGNED_SHORT_5_5_5_1; - convert = CColorConverter::convert_A1R5G5B5toR5G5B5A1; - break; - case ECF_R5G6B5: - InternalFormat = GL_RGB; - PixelFormat = GL_RGB; - PixelType = GL_UNSIGNED_SHORT_5_6_5; - break; - case ECF_R8G8B8: - InternalFormat = GL_RGB; - PixelFormat = GL_RGB; - PixelType = GL_UNSIGNED_BYTE; - convert = CColorConverter::convert_R8G8B8toB8G8R8; - break; - case ECF_A8R8G8B8: - PixelType = GL_UNSIGNED_BYTE; - if (Driver->queryOpenGLFeature(COGLES2ExtensionHandler::IRR_IMG_texture_format_BGRA8888) || - Driver->queryOpenGLFeature( COGLES2ExtensionHandler::IRR_EXT_texture_format_BGRA8888)) - { - InternalFormat = GL_BGRA; - PixelFormat = GL_BGRA; - } - else - { - convert = CColorConverter::convert_A8R8G8B8toA8B8G8R8; - InternalFormat = GL_RGBA; - PixelFormat = GL_RGBA; - } - break; - default: - os::Printer::log( "Unsupported texture format", ELL_ERROR ); - break; + // 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); + + StatesCache.BilinearFilter = true; + StatesCache.TrilinearFilter = false; + StatesCache.MipMapStatus = true; } - - glBindTexture( GL_TEXTURE_2D, TextureName ); - if ( Driver->testGLError() ) - os::Printer::log( "Could not bind Texture", ELL_ERROR ); - - if ( newTexture ) + 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 ); - - StatesCache.BilinearFilter = true; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + StatesCache.BilinearFilter = true; StatesCache.TrilinearFilter = false; StatesCache.MipMapStatus = false; } - - void* source = 0; - IImage* tmpImage = 0; - source = Image->lock(); - if ( convert ) - { - tmpImage = new CImage( Image->getColorFormat(), Image->getDimension() ); - void* dest = tmpImage->lock(); - convert( source, Image->getDimension().getArea(), dest ); - Image->unlock(); - source = dest; - } - if ( newTexture ) - glTexImage2D(GL_TEXTURE_2D, 0, InternalFormat, Image->getDimension().Width, - Image->getDimension().Height, 0, PixelFormat, PixelType, source); - else - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, Image->getDimension().Width, - Image->getDimension().Height, PixelFormat, PixelType, source); - if ( convert ) - { - tmpImage->unlock(); - tmpImage->drop(); - } - else - Image->unlock(); - - if ( Driver->testGLError() ) - os::Printer::log( "Could not glTexImage2D", ELL_ERROR ); - - if ( newTexture ) - { -#ifndef DISABLE_MIPMAPPING - { - AutomaticMipmapUpdate = false; - regenerateMipMapLevels(); - } -#else - HasMipMaps = false; - os::Printer::log( "Did not create OGLES2 texture mip maps.", ELL_ERROR ); -#endif - } } + // now get image data and upload to GPU + void* source = image->lock(); + IImage* tmpImage=0; - //! lock function - /** TODO: support miplevel */ - void* COGLES2Texture::lock(E_TEXTURE_LOCK_MODE mode, u32 mipmapLevel) + if (convert) { - ReadOnlyLock |= (mode==ETLM_READ_ONLY); - - if ( !Image ) - Image = new CImage( ECF_A8R8G8B8, ImageSize ); - if (mode != ETLM_WRITE_ONLY) - { - u8* pPixels = static_cast( Image->lock() ); - if ( !pPixels ) - { - return 0; - } - // we need to keep the correct texture bound... - GLint tmpTexture; - glGetIntegerv( GL_TEXTURE_BINDING_2D, &tmpTexture ); - glBindTexture( GL_TEXTURE_2D, TextureName ); - - // TODO ogl-es - // glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_BYTE, pPixels); - - // opengl images are horizontally flipped, so we have to fix that here. - const u32 pitch = Image->getPitch(); - u8* p2 = pPixels + ( ImageSize.Height - 1 ) * pitch; - u8* tmpBuffer = new u8[pitch]; - for ( u32 i = 0; i < ImageSize.Height; i += 2 ) - { - memcpy( tmpBuffer, pPixels, pitch ); - memcpy( pPixels, p2, pitch ); - memcpy( p2, tmpBuffer, pitch ); - pPixels += pitch; - p2 -= pitch; - } - delete [] tmpBuffer; - Image->unlock(); - - //reset old bound texture - glBindTexture( GL_TEXTURE_2D, tmpTexture ); - } - return Image->lock(); + tmpImage = new CImage(image->getColorFormat(), image->getDimension()); + void* dest = tmpImage->lock(); + convert(source, image->getDimension().getArea(), dest); + image->unlock(); + source = dest; } + 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); - //! unlock function - void COGLES2Texture::unlock() + if (convert) { - Image->unlock(); - if ( !ReadOnlyLock ) - copyTexture( false ); - ReadOnlyLock = false; + tmpImage->unlock(); + tmpImage->drop(); } + else + image->unlock(); + + if (AutomaticMipmapUpdate) + glGenerateMipmap(GL_TEXTURE_2D); + + if (Driver->testGLError()) + os::Printer::log("Could not glTexImage2D", ELL_ERROR); +} - //! Returns size of the original image. - const core::dimension2d& COGLES2Texture::getOriginalSize() const +//! lock function +void* COGLES2Texture::lock(E_TEXTURE_LOCK_MODE mode, u32 mipmapLevel) +{ + // store info about which image is locked + IImage* image = (mipmapLevel==0)?Image:MipImage; + + return image->lock(); +} + + +//! unlock function +void COGLES2Texture::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) { - return ImageSize; + MipImage->drop(); + MipImage=0; } - - - //! Returns size of the texture. - const core::dimension2d& COGLES2Texture::getSize() const + else if (!KeepImage) { - if ( Image ) - return Image->getDimension(); - else - return ImageSize; + Image->drop(); + Image=0; } + // update information + if (Image) + ColorFormat=Image->getColorFormat(); + else + ColorFormat=ECF_A8R8G8B8; +} - //! returns driver type of texture, i.e. the driver, which created the texture - E_DRIVER_TYPE COGLES2Texture::getDriverType() const +//! Returns size of the original image. +const core::dimension2d& COGLES2Texture::getOriginalSize() const +{ + return ImageSize; +} + + +//! Returns size of the texture. +const core::dimension2d& COGLES2Texture::getSize() const +{ + return TextureSize; +} + + +//! returns driver type of texture, i.e. the driver, which created the texture +E_DRIVER_TYPE COGLES2Texture::getDriverType() const +{ + return EDT_OGLES2; +} + + +//! returns color format of texture +ECOLOR_FORMAT COGLES2Texture::getColorFormat() const +{ + return ColorFormat; +} + + +//! returns pitch of texture (in bytes) +u32 COGLES2Texture::getPitch() const +{ + if (Image) + return Image->getPitch(); + else + return 0; +} + + +//! return open gl texture name +GLuint COGLES2Texture::getOpenGLTextureName() const +{ + return TextureName; +} + + +//! Returns whether this texture has mipmaps +bool COGLES2Texture::hasMipMaps() const +{ + return HasMipMaps; +} + + +//! Regenerates the mip map levels of the texture. Useful after locking and +//! modifying the texture +void COGLES2Texture::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 { - return EDT_OGLES2; - } - - - //! returns color format of texture - ECOLOR_FORMAT COGLES2Texture::getColorFormat() const - { - if ( Image ) - return Image->getColorFormat(); - else - return ECF_A8R8G8B8; - } - - - //! returns pitch of texture (in bytes) - u32 COGLES2Texture::getPitch() const - { - if ( Image ) - return Image->getPitch(); - else - return 0; - } - - - //! return open gl texture name - GLuint COGLES2Texture::getOGLES2TextureName() const - { - return TextureName; - } - - - //! Returns whether this texture has mipmaps - bool COGLES2Texture::hasMipMaps() const - { - return HasMipMaps; - } - - - //! Regenerates the mip map levels of the texture. - void COGLES2Texture::regenerateMipMapLevels(void* mipmapData) - { - if ( AutomaticMipmapUpdate || !HasMipMaps ) - return; - if (( Image->getDimension().Width == 1 ) && ( Image->getDimension().Height == 1 ) ) - return; + 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) { - glGenerateMipmap(GL_TEXTURE_2D); - return; + mipmapData = static_cast(mipmapData)+width*height*Image->getBytesPerPixel(); + target = static_cast(mipmapData); } - - // Manually create mipmaps - 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; - glTexImage2D(GL_TEXTURE_2D, i, InternalFormat, width, height, - 0, PixelFormat, PixelType, mipmapData); - // 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 ); } + while (width!=1 || height!=1); + // cleanup + if (!mipmapData) + delete [] target; +} - bool COGLES2Texture::isRenderTarget() const +bool COGLES2Texture::isRenderTarget() const +{ + return IsRenderTarget; +} + + +void COGLES2Texture::setIsRenderTarget(bool isTarget) +{ + IsRenderTarget = isTarget; +} + + +bool COGLES2Texture::isFrameBufferObject() const +{ + return false; +} + + +//! Bind Render Target Texture +void COGLES2Texture::bindRTT() +{ +} + + +//! Unbind Render Target Texture +void COGLES2Texture::unbindRTT() +{ + Driver->setActiveTexture(0, this); + Driver->getBridgeCalls()->setTexture(0); + + // Copy Our ViewPort To The Texture + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, getSize().Width, getSize().Height); +} + + +//! Get an access to texture states cache. +COGLES2Texture::SStatesCache& COGLES2Texture::getStatesCache() const +{ + return StatesCache; +} + + +/* FBO Textures */ + +// helper function for render to texture +static bool checkOGLES2FBOStatus(COGLES2Driver* Driver); + +//! RTT ColorFrameBuffer constructor +COGLES2FBOTexture::COGLES2FBOTexture(const core::dimension2d& size, + const io::path& name, COGLES2Driver* driver, + ECOLOR_FORMAT format) + : COGLES2Texture(name, driver), DepthTexture(0), ColorFrameBuffer(0) +{ + #ifdef _DEBUG + setDebugName("COGLES2Texture_FBO"); + #endif + + ImageSize = size; + TextureSize = size; + HasMipMaps = false; + IsRenderTarget = true; + ColorFormat = getBestColorFormat(format); + + switch (ColorFormat) { - return IsRenderTarget; - } - - - bool COGLES2Texture::isFrameBufferObject() const - { - return false; - } - - - void COGLES2Texture::setIsRenderTarget( bool isTarget ) - { - IsRenderTarget = isTarget; - } - - - //! Bind Render Target Texture - void COGLES2Texture::bindRTT() - { - glViewport( 0, 0, getSize().Width, getSize().Height ); - } - - - //! Unbind Render Target Texture - void COGLES2Texture::unbindRTT() - { - glBindTexture( GL_TEXTURE_2D, getOGLES2TextureName() ); - - // Copy Our ViewPort To The Texture - glCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, getSize().Width, getSize().Height ); - } - - //! Get an access to texture states cache. - COGLES2Texture::SStatesCache& COGLES2Texture::getStatesCache() const - { - return StatesCache; - } - - /* FBO Textures */ - - // helper function for render to texture - static bool checkFBOStatus( COGLES2Driver* Driver ); - - //! RTT ColorFrameBuffer constructor - COGLES2FBOTexture::COGLES2FBOTexture( const core::dimension2d& size, - const io::path& name, COGLES2Driver* driver, ECOLOR_FORMAT format ) - : COGLES2Texture( name, driver ), DepthTexture( 0 ), ColorFrameBuffer( 0 ) - { -#ifdef _DEBUG - setDebugName( "COGLES2Texture_FBO" ); -#endif - - ECOLOR_FORMAT col = getBestColorFormat( format ); - switch ( col ) - { - case ECF_A8R8G8B8: - InternalFormat = GL_RGBA; - PixelFormat = GL_RGBA; - PixelType = GL_UNSIGNED_BYTE; - break; - case ECF_R8G8B8: - InternalFormat = GL_RGB; - PixelFormat = GL_RGB; - PixelType = GL_UNSIGNED_BYTE; - break; - case ECF_A1R5G5B5: - InternalFormat = GL_RGBA; - PixelFormat = GL_RGBA; - PixelType = GL_UNSIGNED_SHORT_5_5_5_1; - break; - case ECF_R5G6B5: - InternalFormat = GL_RGB; - PixelFormat = GL_RGB; - PixelType = GL_UNSIGNED_SHORT_5_6_5; - break; - default: - os::Printer::log( "color format not handled", ELL_WARNING ); - break; - } - ImageSize = size; - HasMipMaps = false; - IsRenderTarget = true; - - // generate frame buffer - glGenFramebuffers( 1, &ColorFrameBuffer ); - glBindFramebuffer( GL_FRAMEBUFFER, ColorFrameBuffer ); - - // generate color texture - glGenTextures(1, &TextureName); - glBindTexture(GL_TEXTURE_2D, TextureName); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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); - - StatesCache.BilinearFilter = true; - StatesCache.WrapU = ETC_CLAMP_TO_EDGE; - StatesCache.WrapV = ETC_CLAMP_TO_EDGE; - - glTexImage2D(GL_TEXTURE_2D, 0, InternalFormat, ImageSize.Width, - ImageSize.Height, 0, PixelFormat, PixelType, 0); - - // attach color texture to frame buffer - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, TextureName, 0); - // check the status - if ( !checkFBOStatus( Driver ) ) - { - os::Printer::log( "FBO incomplete" ); - } - unbindRTT(); - } - - - //! destructor - COGLES2FBOTexture::~COGLES2FBOTexture() - { - if ( DepthTexture ) - if ( DepthTexture->drop() ) - Driver->removeDepthTexture( DepthTexture ); - if ( ColorFrameBuffer ) - Driver->deleteFramebuffers( 1, &ColorFrameBuffer ); - } - - - bool COGLES2FBOTexture::isFrameBufferObject() const - { - return true; - } - - - //! Bind Render Target Texture - void COGLES2FBOTexture::bindRTT() - { - if ( ColorFrameBuffer != 0 ) - glBindFramebuffer( GL_FRAMEBUFFER, ColorFrameBuffer ); - } - - - //! Unbind Render Target Texture - void COGLES2FBOTexture::unbindRTT() - { - if ( ColorFrameBuffer != 0 ) - glBindFramebuffer( GL_FRAMEBUFFER, 0 ); - } - - - /* FBO Depth Textures */ - - //! RTT DepthBuffer constructor - COGLES2FBODepthTexture::COGLES2FBODepthTexture( - const core::dimension2d& size, const io::path& name, - COGLES2Driver* driver, bool useStencil) - : COGLES2FBOTexture(size, name, driver), DepthRenderBuffer(0), - StencilRenderBuffer(0), UseStencil(useStencil) - { -#ifdef _DEBUG - setDebugName( "COGLES2TextureFBO_Depth" ); -#endif - -#if defined(GL_OES_depth24) - if (Driver->queryOpenGLFeature(COGLES2ExtensionHandler::IRR_OES_depth24)) - InternalFormat = GL_DEPTH_COMPONENT24_OES; - else -#endif -#if defined(GL_OES_depth32) - if (Driver->queryOpenGLFeature(COGLES2ExtensionHandler::IRR_OES_depth32)) - InternalFormat = GL_DEPTH_COMPONENT32_OES; - else -#endif - InternalFormat = GL_DEPTH_COMPONENT16; + case ECF_A8R8G8B8: + InternalFormat = GL_RGBA; + PixelFormat = GL_RGBA; + PixelType = GL_UNSIGNED_BYTE; + break; + case ECF_R8G8B8: + InternalFormat = GL_RGB; PixelFormat = GL_RGB; PixelType = GL_UNSIGNED_BYTE; - HasMipMaps = false; + break; + break; + case ECF_A1R5G5B5: + InternalFormat = GL_RGBA; + PixelFormat = GL_RGBA; + PixelType = GL_UNSIGNED_SHORT_5_5_5_1; + break; + break; + case ECF_R5G6B5: + InternalFormat = GL_RGB; + PixelFormat = GL_RGB; + PixelType = GL_UNSIGNED_SHORT_5_6_5; + break; + default: + os::Printer::log( "color format not handled", ELL_WARNING ); + break; + } - if ( UseStencil ) - { - glGenRenderbuffers( 1, &StencilRenderBuffer ); - glBindRenderbuffer( GL_RENDERBUFFER, StencilRenderBuffer ); + // generate frame buffer + glGenFramebuffers(1, &ColorFrameBuffer); + bindRTT(); + + // generate color texture + glGenTextures(1, &TextureName); + + Driver->setActiveTexture(0, this); + Driver->getBridgeCalls()->setTexture(0); + + 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); + + StatesCache.BilinearFilter = true; + StatesCache.WrapU = ETC_CLAMP_TO_EDGE; + StatesCache.WrapV = ETC_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 + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, TextureName, 0); +#ifdef _DEBUG + checkOGLES2FBOStatus(Driver); +#endif + + unbindRTT(); +} + + +//! destructor +COGLES2FBOTexture::~COGLES2FBOTexture() +{ + if (DepthTexture) + if (DepthTexture->drop()) + Driver->removeDepthTexture(DepthTexture); + if (ColorFrameBuffer) + glDeleteFramebuffers(1, &ColorFrameBuffer); +} + + +bool COGLES2FBOTexture::isFrameBufferObject() const +{ + return true; +} + + +//! Bind Render Target Texture +void COGLES2FBOTexture::bindRTT() +{ + if (ColorFrameBuffer != 0) + glBindFramebuffer(GL_FRAMEBUFFER, ColorFrameBuffer); +} + + +//! Unbind Render Target Texture +void COGLES2FBOTexture::unbindRTT() +{ + if (ColorFrameBuffer != 0) + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + + +/* FBO Depth Textures */ + +//! RTT DepthBuffer constructor +COGLES2FBODepthTexture::COGLES2FBODepthTexture( + const core::dimension2d& size, + const io::path& name, + COGLES2Driver* driver, + bool useStencil) + : COGLES2Texture(name, driver), DepthRenderBuffer(0), + StencilRenderBuffer(0), UseStencil(useStencil) +{ +#ifdef _DEBUG + setDebugName("COGLES2TextureFBO_Depth"); +#endif + + ImageSize = size; + TextureSize = size; + InternalFormat = GL_RGBA; + PixelFormat = GL_RGBA; + PixelType = GL_UNSIGNED_BYTE; + HasMipMaps = false; + + if (useStencil) + { + glGenRenderbuffers(1, &DepthRenderBuffer); + glBindRenderbuffer(GL_RENDERBUFFER, DepthRenderBuffer); #ifdef GL_OES_packed_depth_stencil - if (Driver->queryOpenGLFeature( COGLES2ExtensionHandler::IRR_OES_packed_depth_stencil)) - { - // generate packed depth stencil texture - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, - ImageSize.Width, ImageSize.Height); - StencilRenderBuffer = DepthRenderBuffer; // stencil is packed with depth - return; - } -#endif - // generate stencil buffer - GLenum internalf = GL_STENCIL_INDEX8; -#if 0 // only of use if we can reduce the stencil precision by parameters -#if defined(GL_OES_stencil4) - if (Driver->queryOpenGLFeature(COGLES2ExtensionHandler::IRR_OES_stencil4)) - internalf=GL_STENCIL_INDEX4_OES; -#endif -#if defined(GL_OES_stencil1) - if (Driver->queryOpenGLFeature(COGLES2ExtensionHandler::IRR_OES_stencil1)) - internalf=GL_STENCIL_INDEX1_OES; -#endif -#endif - glRenderbufferStorage(GL_RENDERBUFFER, internalf, - ImageSize.Width, ImageSize.Height); + if (Driver->queryOpenGLFeature(COGLES2ExtensionHandler::IRR_OES_packed_depth_stencil)) + { + // generate packed depth stencil buffer + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, ImageSize.Width, ImageSize.Height); + StencilRenderBuffer = DepthRenderBuffer; // stencil is packed with depth } + else // generate separate stencil and depth textures +#endif + { + glRenderbufferStorage(GL_RENDERBUFFER, Driver->getZBufferBits(), ImageSize.Width, ImageSize.Height); + + glGenRenderbuffers(1, &StencilRenderBuffer); + glBindRenderbuffer(GL_RENDERBUFFER, StencilRenderBuffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, ImageSize.Width, ImageSize.Height); + } + } + else + { // generate depth buffer glGenRenderbuffers(1, &DepthRenderBuffer); glBindRenderbuffer(GL_RENDERBUFFER, DepthRenderBuffer); - glRenderbufferStorage(GL_RENDERBUFFER, - InternalFormat, ImageSize.Width, ImageSize.Height); + glRenderbufferStorage(GL_RENDERBUFFER, Driver->getZBufferBits(), ImageSize.Width, ImageSize.Height); } +} - //! destructor - COGLES2FBODepthTexture::~COGLES2FBODepthTexture() +//! destructor +COGLES2FBODepthTexture::~COGLES2FBODepthTexture() +{ + if (DepthRenderBuffer) + glDeleteRenderbuffers(1, &DepthRenderBuffer); + + if (StencilRenderBuffer && StencilRenderBuffer != DepthRenderBuffer) + glDeleteRenderbuffers(1, &StencilRenderBuffer); +} + + +//combine depth texture and rtt +bool COGLES2FBODepthTexture::attach(ITexture* renderTex) +{ + if (!renderTex) + return false; + COGLES2FBOTexture* rtt = static_cast(renderTex); + rtt->bindRTT(); + + // attach stencil texture to stencil buffer + if (UseStencil) + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, StencilRenderBuffer); + + // attach depth renderbuffer to depth buffer + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, DepthRenderBuffer); + + // check the status + if (!checkOGLES2FBOStatus(Driver)) { - Driver->deleteRenderbuffers(1, &DepthRenderBuffer); - if ( StencilRenderBuffer && (StencilRenderBuffer != DepthRenderBuffer)) - Driver->deleteRenderbuffers(1, &StencilRenderBuffer); - } - - - //! combine depth texture and rtt - bool COGLES2FBODepthTexture::attach(ITexture* renderTex) - { - if ( !renderTex ) - return false; - video::COGLES2FBOTexture* rtt = static_cast( renderTex ); - rtt->bindRTT(); - if ( UseStencil ) - { - // attach stencil texture to stencil buffer - glFramebufferRenderbuffer(GL_FRAMEBUFFER, - GL_STENCIL_ATTACHMENT, - GL_RENDERBUFFER, StencilRenderBuffer); - - } - // attach depth renderbuffer to depth buffer - glFramebufferRenderbuffer(GL_FRAMEBUFFER, - GL_DEPTH_ATTACHMENT, - GL_RENDERBUFFER, DepthRenderBuffer); - // 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 COGLES2FBODepthTexture::bindRTT() - { - } - - - //! Unbind Render Target Texture - void COGLES2FBODepthTexture::unbindRTT() - { - } - - - bool checkFBOStatus( COGLES2Driver* Driver ) - { - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - - switch ( status ) - { - //Our FBO is perfect, return true - case GL_FRAMEBUFFER_COMPLETE: - return true; - - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: - os::Printer::log( "FBO has one or several incomplete image attachments", ELL_ERROR ); - break; - - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: - os::Printer::log( "FBO missing an image attachment", ELL_ERROR ); - break; - - case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: - os::Printer::log( "FBO has one or several image attachments with different dimensions", ELL_ERROR ); - break; - -#ifdef GL_FRAMEBUFFER_INCOMPLETE_FORMATS - case GL_FRAMEBUFFER_INCOMPLETE_FORMATS: - os::Printer::log( "FBO has one or several image attachments with different internal formats", ELL_ERROR ); - break; -#endif - -#ifdef GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER - // not part of all implementations - case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: - os::Printer::log( "FBO has invalid draw buffer", ELL_ERROR ); - break; -#endif - -#ifdef GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER - // not part of all implementations - case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: - os::Printer::log( "FBO has invalid read buffer", ELL_ERROR ); - break; -#endif - -#ifdef GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT - // not part of fbo_object anymore, but won't harm as it is just a return value - case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT: - os::Printer::log( "FBO has a duplicate image attachment", ELL_ERROR ); - break; -#endif - - case GL_FRAMEBUFFER_UNSUPPORTED: - os::Printer::log( "FBO format unsupported", ELL_ERROR ); - break; - - default: - break; - } - os::Printer::log( "FBO error", ELL_ERROR ); + 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 COGLES2FBODepthTexture::bindRTT() +{ +} + + +//! Unbind Render Target Texture +void COGLES2FBODepthTexture::unbindRTT() +{ +} + + +bool checkOGLES2FBOStatus(COGLES2Driver* Driver) +{ + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + switch (status) + { + case GL_FRAMEBUFFER_COMPLETE: + return true; + + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + os::Printer::log("FBO has one or several incomplete image attachments", ELL_ERROR); + break; + + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + os::Printer::log("FBO missing an image attachment", ELL_ERROR); + break; + + case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: + os::Printer::log("FBO has one or several image attachments with different dimensions", ELL_ERROR); + break; + + case GL_FRAMEBUFFER_UNSUPPORTED: + os::Printer::log("FBO format unsupported", ELL_ERROR); + break; + + default: + break; + } + + os::Printer::log("FBO error", ELL_ERROR); + + return false; +} + } // end namespace video } // end namespace irr diff --git a/source/Irrlicht/COGLES2Texture.h b/source/Irrlicht/COGLES2Texture.h index 178726e1..8fd1d738 100644 --- a/source/Irrlicht/COGLES2Texture.h +++ b/source/Irrlicht/COGLES2Texture.h @@ -8,180 +8,196 @@ #ifndef __C_OGLES2_TEXTURE_H_INCLUDED__ #define __C_OGLES2_TEXTURE_H_INCLUDED__ -#include "SMaterialLayer.h" +#include "IrrCompileConfig.h" + +#ifdef _IRR_COMPILE_WITH_OGLES2_ + +#if defined(_IRR_COMPILE_WITH_IPHONE_DEVICE_) +#include +#else +#include +#endif + #include "ITexture.h" #include "IImage.h" - -#include "IrrCompileConfig.h" -#ifdef _IRR_COMPILE_WITH_OGLES2_ +#include "SMaterialLayer.h" namespace irr { namespace video { - class COGLES2Driver; +class COGLES2Driver; - //! OGLES2 texture. - class COGLES2Texture : public ITexture +//! OpenGL ES 2.0 texture. +class COGLES2Texture : public ITexture +{ +public: + + //! Cache structure. + struct SStatesCache { - public: - - //! Cache structure. - struct SStatesCache + SStatesCache() : WrapU(ETC_REPEAT), WrapV(ETC_REPEAT), BilinearFilter(false), + TrilinearFilter(false), AnisotropicFilter(0), MipMapStatus(false), IsCached(false), LODBias(0) { - SStatesCache() : WrapU(ETC_REPEAT), WrapV(ETC_REPEAT), BilinearFilter(false), - TrilinearFilter(false), AnisotropicFilter(0), MipMapStatus(false), IsCached(false), LODBias(0) - { - } + } - u8 WrapU; - u8 WrapV; - bool BilinearFilter; - bool TrilinearFilter; - u8 AnisotropicFilter; - bool MipMapStatus; - s8 LODBias; + u8 WrapU; + u8 WrapV; + bool BilinearFilter; + bool TrilinearFilter; + u8 AnisotropicFilter; + bool MipMapStatus; + s8 LODBias; - bool IsCached; - }; - - //! constructor - COGLES2Texture(IImage* surface, const io::path& name, COGLES2Driver* driver = 0); - - //! destructor - virtual ~COGLES2Texture(); - - //! lock function - virtual void* lock(E_TEXTURE_LOCK_MODE mode=ETLM_READ_WRITE, u32 mipmapLevel=0); - - //! unlock function - virtual void unlock(); - - //! Returns original size of the texture (image). - virtual const core::dimension2d& getOriginalSize() const; - - //! Returns size of the texture. - virtual const core::dimension2d& getSize() const; - - //! returns driver type of texture (=the driver, that created it) - virtual E_DRIVER_TYPE getDriverType() const; - - //! returns color format of texture - virtual ECOLOR_FORMAT getColorFormat() const; - - //! returns pitch of texture (in bytes) - virtual u32 getPitch() const; - - //! return open gl texture name - u32 getOGLES2TextureName() const; - - //! return whether this texture has mipmaps - virtual bool hasMipMaps() const; - - //! Regenerates the mip map levels of the texture. - virtual void regenerateMipMapLevels(void* mipmapData=0); - - //! Is it a render target? - virtual bool isRenderTarget() const; - - //! Is it a FrameBufferObject? - virtual bool isFrameBufferObject() const; - - //! Bind RenderTargetTexture - void bindRTT(); - - //! Unbind RenderTargetTexture - void unbindRTT(); - - //! sets whether this texture is intended to be used as a render target. - void setIsRenderTarget(bool isTarget); - - //! Get an access to texture states cache. - SStatesCache& getStatesCache() const; - - protected: - - //! protected constructor with basic setup, no GL texture name created, for derived classes - COGLES2Texture(const io::path& name, COGLES2Driver* driver); - - //! get the desired color format based on texture creation flags and the input format. - ECOLOR_FORMAT getBestColorFormat(ECOLOR_FORMAT format); - - //! convert the image into an internal image with better properties for this driver. - void getImageData(IImage* image); - - //! copies the the texture into an open gl texture. - void copyTexture(bool newTexture = true); - - core::dimension2d ImageSize; - COGLES2Driver* Driver; - IImage* Image; - - u32 TextureName; - s32 InternalFormat; - u32 PixelFormat; - u32 PixelType; - - bool HasMipMaps; - bool IsRenderTarget; - bool AutomaticMipmapUpdate; - bool UseStencil; - bool ReadOnlyLock; - - mutable SStatesCache StatesCache; + bool IsCached; }; + //! constructor + COGLES2Texture(IImage* surface, const io::path& name, void* mipmapData=0, COGLES2Driver* driver=0); - //! OGLES2 FBO texture. - class COGLES2FBOTexture : public COGLES2Texture - { - public: + //! destructor + virtual ~COGLES2Texture(); - //! FrameBufferObject constructor - COGLES2FBOTexture(const core::dimension2d& size, const io::path& name, COGLES2Driver* driver = 0, ECOLOR_FORMAT format = ECF_UNKNOWN); + //! lock function + virtual void* lock(E_TEXTURE_LOCK_MODE mode=ETLM_READ_WRITE, u32 mipmapLevel=0); - //! destructor - virtual ~COGLES2FBOTexture(); + //! unlock function + virtual void unlock(); - //! Is it a FrameBufferObject? - virtual bool isFrameBufferObject() const; + //! Returns original size of the texture (image). + virtual const core::dimension2d& getOriginalSize() const; - //! Bind RenderTargetTexture - virtual void bindRTT(); + //! Returns size of the texture. + virtual const core::dimension2d& getSize() const; - //! Unbind RenderTargetTexture - virtual void unbindRTT(); + //! returns driver type of texture (=the driver, that created it) + virtual E_DRIVER_TYPE getDriverType() const; - ITexture* DepthTexture; - protected: - u32 ColorFrameBuffer; - }; + //! returns color format of texture + virtual ECOLOR_FORMAT getColorFormat() const; + + //! returns pitch of texture (in bytes) + virtual u32 getPitch() const; + + //! return open gl texture name + GLuint getOpenGLTextureName() const; + + //! return whether this texture has mipmaps + virtual bool hasMipMaps() const; + + //! Regenerates the mip map levels of the texture. + /** Useful after locking and modifying the texture + \param mipmapData Pointer to raw mipmap data, including all necessary mip levels, in the same format as the main texture image. If not set the mipmaps are derived from the main image. */ + virtual void regenerateMipMapLevels(void* mipmapData=0); + + //! Is it a render target? + virtual bool isRenderTarget() const; + + //! Is it a FrameBufferObject? + virtual bool isFrameBufferObject() const; + + //! Bind RenderTargetTexture + virtual void bindRTT(); + + //! Unbind RenderTargetTexture + virtual void unbindRTT(); + + //! sets whether this texture is intended to be used as a render target. + void setIsRenderTarget(bool isTarget); + + //! Get an access to texture states cache. + SStatesCache& getStatesCache() const; + +protected: + + //! protected constructor with basic setup, no GL texture name created, for derived classes + COGLES2Texture(const io::path& name, COGLES2Driver* driver); + + //! get the desired color format based on texture creation flags and the input format. + ECOLOR_FORMAT getBestColorFormat(ECOLOR_FORMAT format); + + //! get important numbers of the image and hw texture + void getImageValues(IImage* image); + + //! copies the texture into an OpenGL texture. + /** \param newTexture True if method is called for a newly created texture for the first time. Otherwise call with false to improve memory handling. + \param mipmapData Pointer to raw mipmap data, including all necessary mip levels, in the same format as the main texture image. + \param mipLevel If set to non-zero, only that specific miplevel is updated, using the MipImage member. */ + void uploadTexture(bool newTexture=false, void* mipmapData=0, u32 mipLevel=0); + + core::dimension2d ImageSize; + core::dimension2d TextureSize; + ECOLOR_FORMAT ColorFormat; + COGLES2Driver* Driver; + IImage* Image; + IImage* MipImage; + + GLuint TextureName; + GLint InternalFormat; + GLenum PixelFormat; + GLenum PixelType; + + u8 MipLevelStored; + bool HasMipMaps; + bool IsRenderTarget; + bool AutomaticMipmapUpdate; + bool ReadOnlyLock; + bool KeepImage; + + mutable SStatesCache StatesCache; +}; + +//! OpenGL ES 2.0 FBO texture. +class COGLES2FBOTexture : public COGLES2Texture +{ +public: + + //! FrameBufferObject constructor + COGLES2FBOTexture(const core::dimension2d& size, const io::path& name, + COGLES2Driver* driver = 0, const ECOLOR_FORMAT format = ECF_UNKNOWN); + + //! destructor + virtual ~COGLES2FBOTexture(); + + //! Is it a FrameBufferObject? + virtual bool isFrameBufferObject() const; + + //! Bind RenderTargetTexture + virtual void bindRTT(); + + //! Unbind RenderTargetTexture + virtual void unbindRTT(); + + ITexture* DepthTexture; +protected: + GLuint ColorFrameBuffer; +}; - //! OGLES2 FBO depth texture. - class COGLES2FBODepthTexture : public COGLES2FBOTexture - { - public: - //! FrameBufferObject depth constructor - COGLES2FBODepthTexture(const core::dimension2d& size, const io::path& name, COGLES2Driver* driver = 0, bool useStencil = false); +//! OpenGL ES 2.0 FBO depth texture. +class COGLES2FBODepthTexture : public COGLES2Texture +{ +public: + //! FrameBufferObject depth constructor + COGLES2FBODepthTexture(const core::dimension2d& size, const io::path& name, COGLES2Driver* driver=0, bool useStencil=false); - //! destructor - virtual ~COGLES2FBODepthTexture(); + //! destructor + virtual ~COGLES2FBODepthTexture(); - //! Bind RenderTargetTexture - virtual void bindRTT(); + //! Bind RenderTargetTexture + virtual void bindRTT(); - //! Unbind RenderTargetTexture - virtual void unbindRTT(); + //! Unbind RenderTargetTexture + virtual void unbindRTT(); - bool attach(ITexture* rtt); + bool attach(ITexture*); - protected: - u32 DepthRenderBuffer; - u32 StencilRenderBuffer; - bool UseStencil; - }; +protected: + GLuint DepthRenderBuffer; + GLuint StencilRenderBuffer; + bool UseStencil; +}; } // end namespace video