Completed support for assigning 3D osg::Image to osg::Texture2DArray.

Improved the osgtexture2DArray example to add --mipmap and --packed command line options to help with testing.


git-svn-id: http://svn.openscenegraph.org/osg/OpenSceneGraph/trunk@14775 16af8721-9629-0410-8352-f15c8da7e697
This commit is contained in:
Robert Osfield
2015-03-11 17:27:08 +00:00
parent b6acb1a8d3
commit 75b9cd3627
3 changed files with 122 additions and 113 deletions

View File

@@ -25,6 +25,7 @@
#include <osgDB/Registry>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgViewer/Viewer>
@@ -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"<<std::endl;
texture->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."<<std::endl;
@@ -88,8 +96,6 @@ osg::StateSet* createState(osg::ArgumentParser& arguments)
image_3d->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<osg::TexGen*>(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<osg::Node> model = createModel(arguments);
if (!model) return 0;
std::string filename;
if (arguments.read("-o",filename))
{
osg::ref_ptr<osgDB::Options> 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();
}

View File

@@ -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<Image> > Images;
Images _images;

View File

@@ -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<static_cast<unsigned int>(_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<static_cast<unsigned int>(_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;
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<unsigned int>(_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<Texture2DArray*>(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."<<std::endl;
// the image object does provide mipmaps, so upload the in the certain levels of a layer
}else
}
else
{
numMipmapLevels = image->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<GLExtensions>();
@@ -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);
}