diff --git a/examples/osgtexture2DArray/osgtexture2DArray.cpp b/examples/osgtexture2DArray/osgtexture2DArray.cpp index bbd469fb7..409df5669 100644 --- a/examples/osgtexture2DArray/osgtexture2DArray.cpp +++ b/examples/osgtexture2DArray/osgtexture2DArray.cpp @@ -25,6 +25,7 @@ #include #include +#include #include @@ -72,6 +73,13 @@ osg::StateSet* createState(osg::ArgumentParser& arguments) texture->setFilter(osg::Texture2DArray::MAG_FILTER, osg::Texture2DArray::LINEAR); texture->setWrap(osg::Texture2DArray::WRAP_R, osg::Texture2DArray::REPEAT); + if (arguments.read("--mipmap")) + { + OSG_NOTICE<<"Enabling Mipmaping"<setUseHardwareMipMapGeneration(true); + texture->setFilter(osg::Texture2DArray::MIN_FILTER, osg::Texture2DArray::LINEAR_MIPMAP_LINEAR); + } + if (arguments.read("--packed")) { OSG_NOTICE<<"Packing all images into a single osg::Image to pass to Texture2DArray."<setInternalTextureFormat(image_0->getInternalTextureFormat()); texture->setImage(0, image_3d.get()); - - texture->setTextureDepth(4); } else { @@ -99,7 +105,6 @@ osg::StateSet* createState(osg::ArgumentParser& arguments) texture->setImage(1, image_1.get()); texture->setImage(2, image_2.get()); texture->setImage(3, image_3.get()); - texture->setTextureDepth(4); } @@ -125,42 +130,6 @@ osg::StateSet* createState(osg::ArgumentParser& arguments) return stateset; } - -class UpdateStateCallback : public osg::NodeCallback -{ - public: - UpdateStateCallback() {} - - void animateState(osg::StateSet* stateset) - { - // here we simply get any existing texgen, and then increment its - // plane, pushing the R coordinate through the texture. - osg::StateAttribute* attribute = stateset->getTextureAttribute(0,osg::StateAttribute::TEXGEN); - osg::TexGen* texgen = dynamic_cast(attribute); - if (texgen) - { - texgen->getPlane(osg::TexGen::R)[3] += 0.001f; - } - - } - - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) - { - - osg::StateSet* stateset = node->getStateSet(); - if (stateset) - { - // we have an exisitng stateset, so lets animate it. - animateState(stateset); - } - - // note, callback is repsonsible for scenegraph traversal so - // should always include call the traverse(node,nv) to ensure - // that the rest of cullbacks and the scene graph are traversed. - traverse(node,nv); - } -}; - /** create 2,2 square with center at 0,0,0 and aligned along the XZ plan */ osg::Drawable* createSquare() { @@ -192,23 +161,11 @@ osg::Drawable* createSquare() osg::Node* createModel(osg::ArgumentParser& arguments) { - // create the geometry of the model, just a simple 2d quad right now. osg::Geode* geode = new osg::Geode; + geode->addDrawable(createSquare()); - // normally we'd create the stateset's to contain all the textures - // etc here, but, the above technique uses osg::Image::scaleImage and - // osg::Image::copySubImage() which are implemented with OpenGL utility - // library, which unfortunately can't be used until we have a valid - // OpenGL context, and at this point in initilialization we don't have - // a valid OpenGL context, so we have to delay creation of state until - // there is a valid OpenGL context. I'll manage this by using an - // app callback which will create the state during the first traversal. - // A bit hacky, and my plan is to reimplement the osg::scaleImage and - // osg::Image::copySubImage() without using GLU which will get round - // this current limitation. - geode->setUpdateCallback(new UpdateStateCallback()); geode->setStateSet(createState(arguments)); return geode; @@ -220,11 +177,26 @@ int main(int argc, char **argv) { osg::ArgumentParser arguments(&argc, argv); + // create a model from the images and pass it to the viewer. + osg::ref_ptr model = createModel(arguments); + if (!model) return 0; + + std::string filename; + if (arguments.read("-o",filename)) + { + osg::ref_ptr options = new osgDB::Options; + options->setOptionString("WriteImageHint=IncludeData"); + osgDB::writeNodeFile(*model, filename, options.get()); + return 0; + } + + // construct the viewer. osgViewer::Viewer viewer(arguments); - // create a model from the images and pass it to the viewer. - viewer.setSceneData(createModel(arguments)); + // assign scene graph to viewer + viewer.setSceneData(model.get()); + // run viewer return viewer.run(); } diff --git a/include/osg/Texture2DArray b/include/osg/Texture2DArray index 436e9ffa6..2ce170e9f 100644 --- a/include/osg/Texture2DArray +++ b/include/osg/Texture2DArray @@ -127,9 +127,10 @@ class OSG_EXPORT Texture2DArray : public Texture bool imagesValid() const; virtual void computeInternalFormat() const; + GLsizei computeTextureDepth() const; void allocateMipmap(State& state) const; - void applyTexImage2DArray_subload(State& state, Image* image, GLsizei inwidth, GLsizei inheight, GLsizei indepth, GLint inInternalFormat, GLsizei& numMipmapLevels) const; + void applyTexImage2DArray_subload(State& state, Image* image, GLsizei layer, GLsizei inwidth, GLsizei inheight, GLsizei indepth, GLint inInternalFormat, GLsizei& numMipmapLevels) const; typedef std::vector< ref_ptr > Images; Images _images; diff --git a/src/osg/Texture2DArray.cpp b/src/osg/Texture2DArray.cpp index ccb62f474..7e3402243 100644 --- a/src/osg/Texture2DArray.cpp +++ b/src/osg/Texture2DArray.cpp @@ -38,8 +38,7 @@ Texture2DArray::Texture2DArray(const Texture2DArray& text,const CopyOp& copyop): { setTextureDepth(text._textureDepth); - // copy all images by iterating through all of them - for (int i=0; i < text._textureDepth; i++) + for(unsigned int i = 0; i(_images.size()); ++i) { setImage(i, copyop(text._images[i].get())); } @@ -47,7 +46,7 @@ Texture2DArray::Texture2DArray(const Texture2DArray& text,const CopyOp& copyop): Texture2DArray::~Texture2DArray() { - for (int i=0; i<_textureDepth; ++i) + for(unsigned int i = 0; i(_images.size()); ++i) { setImage(i, NULL); } @@ -59,8 +58,11 @@ int Texture2DArray::compare(const StateAttribute& sa) const // used by the COMPARE_StateAttribute_Parameter macros below. COMPARE_StateAttribute_Types(Texture2DArray,sa) + if (_images.size()rhs._images.size()) return 1; + bool noImages = true; - for (int n=0; n < _textureDepth; n++) + for (unsigned int n=0; n < static_cast(_images.size()); n++) { if (noImages && _images[n].valid()) noImages = false; if (noImages && rhs._images[n].valid()) noImages = false; @@ -86,7 +88,6 @@ int Texture2DArray::compare(const StateAttribute& sa) const } } - if (noImages) { int result = compareTextureObjects(rhs); @@ -211,6 +212,22 @@ void Texture2DArray::computeInternalFormat() const else computeInternalFormatType(); } +GLsizei Texture2DArray::computeTextureDepth() const +{ + GLsizei textureDepth = _textureDepth; + if (textureDepth==0) + { + for(Images::const_iterator itr = _images.begin(); + itr != _images.end(); + ++itr) + { + const osg::Image* image = itr->get(); + if (image) textureDepth += image->r(); + } + } + return textureDepth; +} + void Texture2DArray::apply(State& state) const { @@ -234,7 +251,9 @@ void Texture2DArray::apply(State& state) const // get the texture object for the current contextID. TextureObject* textureObject = getTextureObject(contextID); - if (textureObject && _textureDepth>0) + GLsizei textureDepth = computeTextureDepth(); + + if (textureObject && textureDepth>0) { const osg::Image* image = _images[0].get(); if (image && getModifiedCount(0, contextID) != image->getModifiedCount()) @@ -247,7 +266,7 @@ void Texture2DArray::apply(State& state) const // compute the dimensions of the texture. computeRequiredTextureDimensions(state, *image, new_width, new_height, new_numMipmapLevels); - if (!textureObject->match(GL_TEXTURE_2D_ARRAY_EXT, new_numMipmapLevels, _internalFormat, new_width, new_height, _textureDepth, _borderWidth)) + if (!textureObject->match(GL_TEXTURE_2D_ARRAY_EXT, new_numMipmapLevels, _internalFormat, new_width, new_height, textureDepth, _borderWidth)) { Texture::releaseTextureObject(contextID, _textureObjectBuffer[contextID].get()); _textureObjectBuffer[contextID] = 0; @@ -272,19 +291,23 @@ void Texture2DArray::apply(State& state) const } else { - // for each image of the texture array do - for (GLsizei n=0; n < _textureDepth; n++) + GLsizei n = 0; + for(Images::const_iterator itr = _images.begin(); + itr != _images.end(); + ++itr) { - osg::Image* image = _images[n].get(); - - // if image content is modified, then upload it to the GPU memory - if (image && getModifiedCount(n,contextID) != image->getModifiedCount()) + osg::Image* image = itr->get(); + if (image) { - applyTexImage2DArray_subload(state, image, _textureWidth, _textureHeight, n, _internalFormat, _numMipmapLevels); - getModifiedCount(n,contextID) = image->getModifiedCount(); + if (getModifiedCount(n,contextID) != image->getModifiedCount()) + { + applyTexImage2DArray_subload(state, image, n, _textureWidth, _textureHeight, image->r(), _internalFormat, _numMipmapLevels); + getModifiedCount(n,contextID) = image->getModifiedCount(); + } + n += image->r(); } } - } + } } @@ -311,7 +334,7 @@ void Texture2DArray::apply(State& state) const // create texture object textureObject = generateTextureObject( - this, contextID,GL_TEXTURE_2D_ARRAY_EXT,_numMipmapLevels,_internalFormat,_textureWidth,_textureHeight,_textureDepth,0); + this, contextID, GL_TEXTURE_2D_ARRAY_EXT,_numMipmapLevels, _internalFormat, _textureWidth, _textureHeight, textureDepth,0); // bind texture textureObject->bind(); @@ -327,8 +350,8 @@ void Texture2DArray::apply(State& state) const extensions->isCompressedTexImage3DSupported() ) { extensions->glCompressedTexImage3D( GL_TEXTURE_2D_ARRAY_EXT, 0, _internalFormat, - _textureWidth, _textureHeight, _textureDepth, _borderWidth, - _images[0]->getImageSizeInBytes() * _textureDepth, + _textureWidth, _textureHeight, textureDepth, _borderWidth, + _images[0]->getImageSizeInBytes() * textureDepth, 0); } else @@ -339,28 +362,34 @@ void Texture2DArray::apply(State& state) const sourceFormat = GL_RGBA; extensions->glTexImage3D( GL_TEXTURE_2D_ARRAY_EXT, 0, _internalFormat, - _textureWidth, _textureHeight, _textureDepth, _borderWidth, + _textureWidth, _textureHeight, textureDepth, _borderWidth, sourceFormat, _sourceType ? _sourceType : GL_UNSIGNED_BYTE, 0); } - // For certain we have to manually allocate memory for mipmaps if images are compressed - // if not allocated OpenGL will produce errors on mipmap upload. - // I have not tested if this is neccessary for plain texture formats but - // common sense suggests its required as well. - if( _min_filter != LINEAR && _min_filter != NEAREST && _images[0]->isMipmap() ) - allocateMipmap( state ); - - // now for each layer we upload it into the memory - for (GLsizei n=0; n<_textureDepth; n++) + // For certain we have to manually allocate memory for mipmaps if images are compressed + // if not allocated OpenGL will produce errors on mipmap upload. + // I have not tested if this is neccessary for plain texture formats but + // common sense suggests its required as well. + if( _min_filter != LINEAR && _min_filter != NEAREST && _images[0]->isMipmap() ) { - // if image is valid then upload it to the texture memory - osg::Image* image = _images[n].get(); + allocateMipmap( state ); + } + + GLsizei n = 0; + for(Images::const_iterator itr = _images.begin(); + itr != _images.end(); + ++itr) + { + osg::Image* image = itr->get(); if (image) { - // now load the image data into the memory, this will also check if image do have valid properties - applyTexImage2DArray_subload(state, image, _textureWidth, _textureHeight, n, _internalFormat, _numMipmapLevels); - getModifiedCount(n,contextID) = image->getModifiedCount(); + if (getModifiedCount(n,contextID) != image->getModifiedCount()) + { + applyTexImage2DArray_subload(state, image, n, _textureWidth, _textureHeight, image->r(), _internalFormat, _numMipmapLevels); + getModifiedCount(n,contextID) = image->getModifiedCount(); + } + n += image->r(); } } @@ -368,22 +397,25 @@ void Texture2DArray::apply(State& state) const // source images have no mipmamps but we could generate them... if( _min_filter != LINEAR && _min_filter != NEAREST && !_images[0]->isMipmap() && _useHardwareMipMapGeneration && extensions->isGenerateMipMapSupported ) - { - _numMipmapLevels = Image::computeNumberOfMipmapLevels( _textureWidth, _textureHeight ); - generateMipmap( state ); - } + { + _numMipmapLevels = Image::computeNumberOfMipmapLevels( _textureWidth, _textureHeight ); + generateMipmap( state ); + } - textureObject->setAllocated(_numMipmapLevels,_internalFormat,_textureWidth,_textureHeight,_textureDepth,0); + textureObject->setAllocated(_numMipmapLevels, _internalFormat, _textureWidth, _textureHeight, textureDepth,0); // unref image data? if (isSafeToUnrefImageData(state)) { Texture2DArray* non_const_this = const_cast(this); - for (int n=0; n<_textureDepth; n++) + for(Images::iterator itr = non_const_this->_images.begin(); + itr != non_const_this->_images.end(); + ++itr) { - if (_images[n].valid() && _images[n]->getDataVariance()==STATIC) + osg::Image* image = itr->get(); + if (image && image->getDataVariance()==STATIC) { - non_const_this->_images[n] = NULL; + *itr = NULL; } } } @@ -395,7 +427,7 @@ void Texture2DArray::apply(State& state) const { // generate texture _textureObjectBuffer[contextID] = textureObject = generateTextureObject( - this, contextID, GL_TEXTURE_2D_ARRAY_EXT,_numMipmapLevels,_internalFormat,_textureWidth,_textureHeight,_textureDepth,0); + this, contextID, GL_TEXTURE_2D_ARRAY_EXT,_numMipmapLevels,_internalFormat, _textureWidth, _textureHeight, _textureDepth,0); textureObject->bind(); applyTexParameters(GL_TEXTURE_2D_ARRAY_EXT,state); @@ -423,7 +455,7 @@ void Texture2DArray::apply(State& state) const } -void Texture2DArray::applyTexImage2DArray_subload(State& state, Image* image, GLsizei inwidth, GLsizei inheight, GLsizei indepth, GLint inInternalFormat, GLsizei& numMipmapLevels) const +void Texture2DArray::applyTexImage2DArray_subload(State& state, Image* image, GLsizei layer, GLsizei inwidth, GLsizei inheight, GLsizei indepth, GLint inInternalFormat, GLsizei& numMipmapLevels) const { // if we don't have a valid image we can't create a texture! if (!imagesValid()) @@ -484,8 +516,8 @@ void Texture2DArray::applyTexImage2DArray_subload(State& state, Image* image, GL if ( !compressed_image ) { extensions->glTexSubImage3D( target, 0, - 0, 0, indepth, - inwidth, inheight, 1, + 0, 0, layer, + inwidth, inheight, indepth, (GLenum)image->getPixelFormat(), (GLenum)image->getDataType(), image->data() ); @@ -500,15 +532,16 @@ void Texture2DArray::applyTexImage2DArray_subload(State& state, Image* image, GL getCompressedSize(_internalFormat, inwidth, inheight, 1, blockSize,size); extensions->glCompressedTexSubImage3D(target, 0, - 0, 0, indepth, - inwidth, inheight, 1, + 0, 0, layer, + inwidth, inheight, indepth, (GLenum)image->getPixelFormat(), size, image->data()); } // we want to use mipmapping, so enable it - }else + } + else { // image does not provide mipmaps, so we have to create them if( !image->isMipmap() ) @@ -517,7 +550,8 @@ void Texture2DArray::applyTexImage2DArray_subload(State& state, Image* image, GL OSG_WARN<<"Warning: Texture2DArray::applyTexImage2DArray_subload(..) mipmap layer not passed, and auto mipmap generation turned off or not available. Check texture's min/mag filters & hardware mipmap generation."<getNumMipmapLevels(); @@ -534,8 +568,8 @@ void Texture2DArray::applyTexImage2DArray_subload(State& state, Image* image, GL if (height == 0) height = 1; - extensions->glTexSubImage3D( target, k, 0, 0, indepth, - width, height, 1, + extensions->glTexSubImage3D( target, k, 0, 0, layer, + width, height, indepth, (GLenum)image->getPixelFormat(), (GLenum)image->getDataType(), image->getMipmapData(k)); @@ -554,12 +588,12 @@ void Texture2DArray::applyTexImage2DArray_subload(State& state, Image* image, GL if (height == 0) height = 1; - getCompressedSize(image->getInternalTextureFormat(), width, height, 1, blockSize,size); + getCompressedSize(image->getInternalTextureFormat(), width, height, indepth, blockSize,size); // state.checkGLErrors("before extensions->glCompressedTexSubImage3D("); - extensions->glCompressedTexSubImage3D(target, k, 0, 0, indepth, - width, height, 1, + extensions->glCompressedTexSubImage3D(target, k, 0, 0, layer, + width, height, indepth, (GLenum)image->getPixelFormat(), size, image->getMipmapData(k)); @@ -609,7 +643,9 @@ void Texture2DArray::allocateMipmap(State& state) const // get the texture object for the current contextID. TextureObject* textureObject = getTextureObject(contextID); - if (textureObject && _textureWidth != 0 && _textureHeight != 0 && _textureDepth != 0) + GLsizei textureDepth = computeTextureDepth(); + + if (textureObject && _textureWidth != 0 && _textureHeight != 0 && textureDepth != 0) { const GLExtensions* extensions = state.get(); @@ -646,7 +682,7 @@ void Texture2DArray::allocateMipmap(State& state) const { int size = 0, blockSize = 0; - getCompressedSize( _internalFormat, width, height, _textureDepth, blockSize, size); + getCompressedSize( _internalFormat, width, height, textureDepth, blockSize, size); extensions->glCompressedTexImage3D( GL_TEXTURE_2D_ARRAY_EXT, k, _internalFormat, width, height, _textureDepth, _borderWidth, @@ -656,7 +692,7 @@ void Texture2DArray::allocateMipmap(State& state) const else { extensions->glTexImage3D( GL_TEXTURE_2D_ARRAY_EXT, k, _internalFormat, - width, height, _textureDepth, _borderWidth, + width, height, textureDepth, _borderWidth, safeSourceFormat, _sourceType ? _sourceType : GL_UNSIGNED_BYTE, NULL); }