diff --git a/include/osg/Texture b/include/osg/Texture index c708c5e90..e30195cf6 100644 --- a/include/osg/Texture +++ b/include/osg/Texture @@ -153,6 +153,12 @@ class SG_EXPORT Texture : public osg::StateAttribute /** Get the maximum anisotropy value.*/ inline float getMaxAnisotropy() const { return _maxAnisotropy; } + /** Set the hint of whether to use hardware mip map generation where available.*/ + inline void setUseHardwareMipMapGeneration(bool useHardwareMipMapGeneration) { _useHardwareMipMapGeneration = useHardwareMipMapGeneration; } + + /** Get the hint of whether to use hardware mip map generation where available.*/ + inline bool getUseHardwareMipMapGeneration() const { return _useHardwareMipMapGeneration; } + enum InternalFormatMode { USE_IMAGE_DATA_FORMAT, USE_USER_DEFINED_FORMAT, @@ -257,6 +263,7 @@ class SG_EXPORT Texture : public osg::StateAttribute bool isCompressedTexImage2DSupported() const { return _glCompressedTexImage2D!=0; } void glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data) const; + void glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei type, const GLvoid *data) const; protected: @@ -273,6 +280,7 @@ class SG_EXPORT Texture : public osg::StateAttribute GLint _maxTextureSize; void* _glCompressedTexImage2D; + void* _glCompressedTexSubImage2D; }; @@ -289,6 +297,14 @@ class SG_EXPORT Texture : public osg::StateAttribute * but need to ensure that they all use the same low common denominator extensions.*/ static void setExtensions(unsigned int contextID,Extensions* extensions); + /** Helper method which does the creation of the texture itself, but does not set or use texture binding. + * Note, do not call this method directly unless you are implementing your own Subload callback*/ + void applyTexImage2D_load(GLenum target, Image* image, State& state, GLsizei& width, GLsizei& height,GLsizei& numMimpmapLevels) const; + + /** Helper method which subloads images to the texture itself, but does not set or use texture binding. + * Note, do not call this method directly unless you are implementing your own Subload callback*/ + void applyTexImage2D_subload(GLenum target, Image* image, State& state, GLsizei& width, GLsizei& height,GLsizei& numMimpmapLevels) const; + protected : virtual ~Texture(); @@ -302,9 +318,6 @@ class SG_EXPORT Texture : public osg::StateAttribute /** Helper method which does setting of texture paramters. */ void applyTexParameters(GLenum target, State& state) const; - /** Helper method which does the creation of the texture itself, and - * does not set or use texture binding. */ - void applyTexImage2D(GLenum target, Image* image, State& state, GLsizei& width, GLsizei& height,GLsizei& numMimpmapLevels) const; /** return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs.*/ int compareTexture(const Texture& rhs) const; @@ -325,6 +338,7 @@ class SG_EXPORT Texture : public osg::StateAttribute FilterMode _min_filter; FilterMode _mag_filter; float _maxAnisotropy; + bool _useHardwareMipMapGeneration; Vec4 _borderColor; diff --git a/include/osg/buffered_value b/include/osg/buffered_value index 49fcd4bdb..c38d320bb 100644 --- a/include/osg/buffered_value +++ b/include/osg/buffered_value @@ -36,6 +36,8 @@ class buffered_value _array = rhs._array; return *this; } + + inline void setAllElementsTo(const T& t) { std::fill(_array.begin(),_array.end(),t); } inline void clear() { _array.clear(); } @@ -78,6 +80,8 @@ class buffered_object return *this; } + inline void setAllElementsTo(const T& t) { std::fill(_array.begin(),_array.end(),t); } + inline void clear() { _array.clear(); } inline bool empty() const { return _array.empty(); } diff --git a/src/osg/Texture.cpp b/src/osg/Texture.cpp index b79da9244..7b03b8e20 100644 --- a/src/osg/Texture.cpp +++ b/src/osg/Texture.cpp @@ -68,6 +68,7 @@ Texture::Texture(): _min_filter(LINEAR_MIPMAP_LINEAR), // trilinear _mag_filter(LINEAR), _maxAnisotropy(1.0f), + _useHardwareMipMapGeneration(false), _borderColor(0.0, 0.0, 0.0, 0.0), _internalFormatMode(USE_IMAGE_DATA_FORMAT), _internalFormat(0) @@ -82,6 +83,7 @@ Texture::Texture(const Texture& text,const CopyOp& copyop): _min_filter(text._min_filter), _mag_filter(text._mag_filter), _maxAnisotropy(text._maxAnisotropy), + _useHardwareMipMapGeneration(text._useHardwareMipMapGeneration), _borderColor(text._borderColor), _internalFormatMode(text._internalFormatMode), _internalFormat(text._internalFormat) @@ -102,6 +104,7 @@ int Texture::compareTexture(const Texture& rhs) const COMPARE_StateAttribute_Parameter(_min_filter) COMPARE_StateAttribute_Parameter(_mag_filter) COMPARE_StateAttribute_Parameter(_maxAnisotropy) + COMPARE_StateAttribute_Parameter(_useHardwareMipMapGeneration) COMPARE_StateAttribute_Parameter(_internalFormatMode) COMPARE_StateAttribute_Parameter(_internalFormat) @@ -363,7 +366,7 @@ void Texture::applyTexParameters(GLenum target, State& state) const } -void Texture::applyTexImage2D(GLenum target, Image* image, State& state, GLsizei& inwidth, GLsizei& inheight,GLsizei& numMimpmapLevels) const +void Texture::applyTexImage2D_load(GLenum target, Image* image, State& state, GLsizei& inwidth, GLsizei& inheight,GLsizei& numMimpmapLevels) const { // if we don't have a valid image we can't create a texture! if (!image || !image->data()) @@ -373,6 +376,9 @@ void Texture::applyTexImage2D(GLenum target, Image* image, State& state, GLsizei // current OpenGL context. const unsigned int contextID = state.getContextID(); const Extensions* extensions = getExtensions(contextID,true); + + bool generateMipMapSupported = extensions->isGenerateMipMapSupported(); + // update the modified tag to show that it is upto date. getModifiedTag(contextID) = image->getModifiedTag(); @@ -382,7 +388,6 @@ void Texture::applyTexImage2D(GLenum target, Image* image, State& state, GLsizei computeInternalFormat(); // select the internalFormat required for the texture. - bool compressed_internal = isCompressedInternalFormat(_internalFormat); bool compressed_image = isCompressedInternalFormat((GLenum)image->getPixelFormat()); image->ensureValidSizeForTexturing(extensions->maxTextureSize()); @@ -391,9 +396,10 @@ void Texture::applyTexImage2D(GLenum target, Image* image, State& state, GLsizei if( _min_filter == LINEAR || _min_filter == NEAREST ) { - if ( !compressed_internal) + if ( !compressed_image) { numMimpmapLevels = 1; + glTexImage2D( target, 0, _internalFormat, image->s(), image->t(), 0, (GLenum)image->getPixelFormat(), @@ -403,26 +409,15 @@ void Texture::applyTexImage2D(GLenum target, Image* image, State& state, GLsizei } else if (extensions->isCompressedTexImage2DSupported()) { - - if (compressed_image) - { - numMimpmapLevels = 1; - GLint blockSize = ( _internalFormat == GL_COMPRESSED_RGB_S3TC_DXT1_EXT ? 8 : 16 ); - GLint size = ((image->s()+3)/4)*((image->t()+3)/4)*blockSize; - extensions->glCompressedTexImage2D(target, 0, _internalFormat, - image->s(), image->t(),0, - size, - image->data()); - } - else - { - numMimpmapLevels = 1; - glTexImage2D( target, 0, _internalFormat, - image->s(), image->t(), 0, - (GLenum)image->getPixelFormat(), - (GLenum)image->getDataType(), - image->data() ); - } + numMimpmapLevels = 1; + + GLint blockSize = ( _internalFormat == GL_COMPRESSED_RGB_S3TC_DXT1_EXT ? 8 : 16 ); + GLint size = ((image->s()+3)/4)*((image->t()+3)/4)*blockSize; + + extensions->glCompressedTexImage2D(target, 0, _internalFormat, + image->s(), image->t(),0, + size, + image->data()); } } @@ -431,13 +426,47 @@ void Texture::applyTexImage2D(GLenum target, Image* image, State& state, GLsizei if(!image->isMipmap()) { - numMimpmapLevels = 1; + if (_useHardwareMipMapGeneration && generateMipMapSupported) + { + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_TRUE); - gluBuild2DMipmaps( target, _internalFormat, - image->s(),image->t(), - (GLenum)image->getPixelFormat(), (GLenum)image->getDataType(), - image->data() ); + numMimpmapLevels = 1; + + glTexImage2D( target, 0, _internalFormat, + image->s(), image->t(), 0, + (GLenum)image->getPixelFormat(), + (GLenum)image->getDataType(), + image->data() ); + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_FALSE); + + int width = image->s(); + int height = image->t(); + for( numMimpmapLevels = 0 ; (width || height) ; ++numMimpmapLevels) + { + width >>= 1; + height >>= 1; + } + + } + else + { + numMimpmapLevels = 0; + + gluBuild2DMipmaps( target, _internalFormat, + image->s(),image->t(), + (GLenum)image->getPixelFormat(), (GLenum)image->getDataType(), + image->data() ); + + int width = image->s(); + int height = image->t(); + for( numMimpmapLevels = 0 ; (width || height) ; ++numMimpmapLevels) + { + width >>= 1; + height >>= 1; + } + + } } else { @@ -446,7 +475,7 @@ void Texture::applyTexImage2D(GLenum target, Image* image, State& state, GLsizei int width = image->s(); int height = image->t(); - if( !compressed_internal ) + if( !compressed_image ) { for( GLsizei k = 0 ; k < numMimpmapLevels && (width || height) ;k++) { @@ -468,45 +497,22 @@ void Texture::applyTexImage2D(GLenum target, Image* image, State& state, GLsizei } else if (extensions->isCompressedTexImage2DSupported()) { - if ( compressed_image ) + GLint blockSize = ( _internalFormat == GL_COMPRESSED_RGB_S3TC_DXT1_EXT ? 8 : 16 ); + GLint size = 0; + for( GLsizei k = 0 ; k < numMimpmapLevels && (width || height) ;k++) { + if (width == 0) + width = 1; + if (height == 0) + height = 1; - GLint blockSize = ( _internalFormat == GL_COMPRESSED_RGB_S3TC_DXT1_EXT ? 8 : 16 ); - GLint size = 0; - for( GLsizei k = 0 ; k < numMimpmapLevels && (width || height) ;k++) - { - if (width == 0) - width = 1; - if (height == 0) - height = 1; + size = ((width+3)/4)*((height+3)/4)*blockSize; + extensions->glCompressedTexImage2D(target, k, _internalFormat, + width, height, 0, + size, image->getMipmapData(k)); - size = ((width+3)/4)*((height+3)/4)*blockSize; - extensions->glCompressedTexImage2D(target, k, _internalFormat, - width, height, 0, size, image->getMipmapData(k)); - - width >>= 1; - height >>= 1; - } - } - else - { - for( GLsizei k = 0 ; k < numMimpmapLevels && (width || height) ;k++) - { - - if (width == 0) - width = 1; - if (height == 0) - height = 1; - - glTexImage2D( target, k, _internalFormat, - width, height, 0, - (GLenum)image->getPixelFormat(), - (GLenum)image->getDataType(), - image->getMipmapData(k)); - - width >>= 1; - height >>= 1; - } + width >>= 1; + height >>= 1; } } } @@ -518,6 +524,206 @@ void Texture::applyTexImage2D(GLenum target, Image* image, State& state, GLsizei } +void Texture::applyTexImage2D_subload(GLenum target, Image* image, State& state, GLsizei& inwidth, GLsizei& inheight,GLsizei& numMimpmapLevels) const +{ + // if we don't have a valid image we can't create a texture! + if (!image || !image->data()) + return; + + // image size has changed so we have to re-load the image from scratch. + if (image->s()!=inwidth || image->t()!=inheight) + { + applyTexImage2D_subload(target, image, state, inwidth, inheight,numMimpmapLevels); + return; + } + // else image size the same as when loaded so we can go ahead and subload + + + + // get the contextID (user defined ID of 0 upwards) for the + // current OpenGL context. + const unsigned int contextID = state.getContextID(); + const Extensions* extensions = getExtensions(contextID,true); + + bool generateMipMapSupported = extensions->isGenerateMipMapSupported(); + + + // update the modified tag to show that it is upto date. + getModifiedTag(contextID) = image->getModifiedTag(); + + + // compute the internal texture format, this set the _internalFormat to an appropriate value. + computeInternalFormat(); + + // select the internalFormat required for the texture. + bool compressed_image = isCompressedInternalFormat((GLenum)image->getPixelFormat()); + + image->ensureValidSizeForTexturing(extensions->maxTextureSize()); + + glPixelStorei(GL_UNPACK_ALIGNMENT,image->getPacking()); + + if( _min_filter == LINEAR || _min_filter == NEAREST ) + { + if (!compressed_image) + { + glTexSubImage2D( target, 0, + 0, 0, + image->s(), image->t(), + (GLenum)image->getPixelFormat(), + (GLenum)image->getDataType(), + image->data() ); + + } + else if (extensions->isCompressedTexImage2DSupported()) + { + extensions->glCompressedTexSubImage2D(target, 0, + 0,0, + image->s(), image->t(), + (GLenum)image->getPixelFormat(), + (GLenum)image->getDataType(), + image->data()); + } + + } + else + { + if(!image->isMipmap()) + { + + if (_useHardwareMipMapGeneration && generateMipMapSupported) + { + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_TRUE); + + glTexSubImage2D( target, 0, + 0, 0, + image->s(), image->t(), + (GLenum)image->getPixelFormat(), + (GLenum)image->getDataType(), + image->data() ); + + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_FALSE); + + } + else + { + numMimpmapLevels = 0; + + int width = image->s(); + int height = image->t(); + + glPixelStorei(GL_PACK_ALIGNMENT,image->getPacking()); + + unsigned int newTotalSize = osg::Image::computeRowWidthInBytes(width,image->getPixelFormat(),image->getDataType(),image->getPacking())*height; + unsigned char* copyData1 = new unsigned char [newTotalSize]; + unsigned char* copyData2 = new unsigned char [newTotalSize]; + unsigned char* data = image->data(); + + int previous_width = width; + int previous_height = height; + + for( GLsizei k = 0 ; (width || height) ;++k) + { + + if (width == 0) + width = 1; + if (height == 0) + height = 1; + + + if (k>0) + { + if (data!=copyData1) + { + gluScaleImage(image->getPixelFormat(), + previous_width,previous_height,image->getDataType(),data, + width,height,image->getDataType(),copyData1); + data = copyData1; + } + else + { + gluScaleImage(image->getPixelFormat(), + previous_width,previous_height,image->getDataType(),data, + width,height,image->getDataType(),copyData2); + data = copyData2; + } + } + + glTexSubImage2D( target, k, + 0, 0, + width, height, + (GLenum)image->getPixelFormat(), + (GLenum)image->getDataType(), + data); + + previous_width = width; + previous_height = height; + + width >>= 1; + height >>= 1; + } + + delete [] copyData1; + delete [] copyData2; + } + } + else + { + numMimpmapLevels = image->getNumMipmapLevels(); + + int width = image->s(); + int height = image->t(); + + if( !compressed_image ) + { + for( GLsizei k = 0 ; k < numMimpmapLevels && (width || height) ;k++) + { + + if (width == 0) + width = 1; + if (height == 0) + height = 1; + + glTexSubImage2D( target, k, + 0, 0, + width, height, + (GLenum)image->getPixelFormat(), + (GLenum)image->getDataType(), + image->getMipmapData(k)); + + width >>= 1; + height >>= 1; + } + } + else if (extensions->isCompressedTexImage2DSupported()) + { + GLint blockSize = ( _internalFormat == GL_COMPRESSED_RGB_S3TC_DXT1_EXT ? 8 : 16 ); + GLint size = 0; + for( GLsizei k = 0 ; k < numMimpmapLevels && (width || height) ;k++) + { + if (width == 0) + width = 1; + if (height == 0) + height = 1; + + size = ((width+3)/4)*((height+3)/4)*blockSize; + + extensions->glCompressedTexSubImage2D(target, k, + 0, 0, + width, height, + (GLenum)image->getPixelFormat(), + (GLenum)image->getDataType(), + image->getMipmapData(k)); + + width >>= 1; + height >>= 1; + } + } + } + + } + +} + /////////////////////////////////////////////////////////////////////////////////////////////// // Static map to manage the deletion of texture objects are the right time. @@ -612,7 +818,8 @@ void Texture::Extensions::setupGLExtenions() } - _glCompressedTexImage2D = getGLExtensionFuncPtr("glCompressedTexImage2DARB");; + _glCompressedTexImage2D = getGLExtensionFuncPtr("glCompressedTexImage2D","glCompressedTexImage2DARB"); + _glCompressedTexSubImage2D = getGLExtensionFuncPtr("glCompressedTexSubImage2D","glCompressedTexSubImage2DARB");; } @@ -629,3 +836,17 @@ void Texture::Extensions::glCompressedTexImage2D(GLenum target, GLint level, GLe } } + +void Texture::Extensions::glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei type, const GLvoid *data) const +{ + if (_glCompressedTexImage2D) + { + typedef void (APIENTRY * CompressedTexSubImage2DArbProc) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei type, const GLvoid *data); + ((CompressedTexSubImage2DArbProc)_glCompressedTexSubImage2D)(target, level, xoffset, yoffset, width, height, format, type, data); + } + else + { + notify(WARN)<<"Error: glCompressedTexImage2D not supported by OpenGL driver"<getModifiedTag()) { - applyTexImage2D(GL_TEXTURE_2D,_image.get(),state, - _textureWidth, _textureHeight, _numMimpmapLevels); + applyTexImage2D_subload(GL_TEXTURE_2D,_image.get(),state, + _textureWidth, _textureHeight, _numMimpmapLevels); } } @@ -138,8 +142,8 @@ void Texture2D::apply(State& state) const applyTexParameters(GL_TEXTURE_2D,state); - applyTexImage2D(GL_TEXTURE_2D,_image.get(),state, - _textureWidth, _textureHeight, _numMimpmapLevels); + applyTexImage2D_load(GL_TEXTURE_2D,_image.get(),state, + _textureWidth, _textureHeight, _numMimpmapLevels); // in theory the following line is redundent, but in practice // have found that the first frame drawn doesn't apply the textures diff --git a/src/osg/TextureCubeMap.cpp b/src/osg/TextureCubeMap.cpp index 3f8c59dc8..b7fbf43ee 100644 --- a/src/osg/TextureCubeMap.cpp +++ b/src/osg/TextureCubeMap.cpp @@ -209,6 +209,9 @@ void TextureCubeMap::apply(State& state) const { _subloadCallback->subload(*this,state); } + else + { + } } else if (_subloadCallback.valid()) @@ -238,7 +241,8 @@ void TextureCubeMap::apply(State& state) const for (int n=0; n<6; n++) { - applyTexImage2D( faceTarget[n], _images[n].get(), state, _textureWidth, _textureHeight, _numMimpmapLevels); + //applyTexImage2D( faceTarget[n], _images[n].get(), state, _textureWidth, _textureHeight, _numMimpmapLevels); + applyTexImage2D_load( faceTarget[n], _images[n].get(), state, _textureWidth, _textureHeight, _numMimpmapLevels); } diff --git a/src/osgPlugins/osg/Texture.cpp b/src/osgPlugins/osg/Texture.cpp index 47ed9c435..ac4b001d5 100644 --- a/src/osgPlugins/osg/Texture.cpp +++ b/src/osgPlugins/osg/Texture.cpp @@ -83,6 +83,22 @@ bool Texture_readLocalData(Object& obj, Input& fr) iteratorAdvanced = true; } + if (fr[0].matchWord("useHardwareMipMapGeneration")) + { + if (fr[1].matchWord("TRUE")) + { + texture.setUseHardwareMipMapGeneration(true); + fr +=2 ; + iteratorAdvanced = true; + } + else if (fr[1].matchWord("TRUE")) + { + texture.setUseHardwareMipMapGeneration(false); + fr +=2 ; + iteratorAdvanced = true; + } + } + Texture::InternalFormatMode mode; if (fr[0].matchWord("internalFormatMode") && Texture_matchInternalFormatModeStr(fr[1].getStr(),mode)) { @@ -117,7 +133,9 @@ bool Texture_writeLocalData(const Object& obj, Output& fw) fw.indent() << "min_filter " << Texture_getFilterStr(texture.getFilter(Texture::MIN_FILTER)) << std::endl; fw.indent() << "mag_filter " << Texture_getFilterStr(texture.getFilter(Texture::MAG_FILTER)) << std::endl; fw.indent() << "maxAnisotropy " << texture.getMaxAnisotropy() << std::endl; - + + fw.indent() << "useHardwareMipMapGeneration "<< (texture.getUseHardwareMipMapGeneration()?"TRUE":"FALSE") << std::endl; + fw.indent() << "internalFormatMode " << Texture_getInternalFormatModeStr(texture.getInternalFormatMode()) << std::endl; if (texture.getInternalFormatMode()==Texture::USE_USER_DEFINED_FORMAT)