Checking in first cut of new osgUtil::Optimizer::TextureAtlasBuilder class

for building texture atlas for sets of images or textures.
This commit is contained in:
Robert Osfield 2006-08-25 15:53:16 +00:00
parent 179f6100a0
commit 1f8c4874f6
2 changed files with 490 additions and 0 deletions

View File

@ -18,6 +18,7 @@
#include <osg/Matrix>
#include <osg/Geometry>
#include <osg/Transform>
#include <osg/Texture2D>
#include <osgUtil/Export>
@ -569,6 +570,125 @@ class OSGUTIL_EXPORT Optimizer
BillboardNodePathMap _billboards;
};
/** Texture Atlas Builder creates a set of textures/images which each contain multiple images.
* Texture Atlas' are used to make it possible to use much wider batching of data. */
class OSGUTIL_EXPORT TextureAtlasBuilder
{
public:
TextureAtlasBuilder();
void setMaximumAtlasSize(unsigned int width, unsigned int height);
unsigned int getMaximumAtlasWidth() const { return _maximumAtlasWidth; }
unsigned int getMaximumAtlasHeight() const { return _maximumAtlasHeight; }
void setMargin(unsigned int margin);
unsigned int getMargin() const { return _margin; }
void addSource(const osg::Image* image);
void addSource(const osg::Texture2D* texture);
unsigned int getNumSources() const { return _sourceList.size(); }
const osg::Image* getSourceImage(unsigned int i) { return _sourceList[i]->_image.get(); }
const osg::Texture2D* getSourceTexture(unsigned int i) { return _sourceList[i]->_texture.get(); }
void buildAtlas();
osg::Image* getImageAtlas(unsigned int i);
osg::Texture2D* getTextureAtlas(unsigned int i);
osg::Matrix getTextureMatrix(unsigned int i);
osg::Image* getImageAtlas(const osg::Image* image);
osg::Texture2D* getTextureAtlas(const osg::Image* image);
osg::Matrix getTextureMatrix(const osg::Image* image);
osg::Image* getImageAtlas(const osg::Texture2D* image);
osg::Texture2D* getTextureAtlas(const osg::Texture2D* texture);
osg::Matrix getTextureMatrix(const osg::Texture2D* texture);
protected:
unsigned int _maximumAtlasWidth;
unsigned int _maximumAtlasHeight;
unsigned int _margin;
// forward declare
class Atlas;
class Source : public osg::Referenced
{
public:
Source():
_x(0),_y(0),_atlas(0) {}
Source(const osg::Image* image):
_x(0),_y(0),_atlas(0),_image(image) {}
Source(const osg::Texture2D* texture):
_x(0),_y(0),_atlas(0),_texture(texture) { if (texture) _image = texture->getImage(); }
unsigned int _x;
unsigned int _y;
Atlas* _atlas;
osg::ref_ptr<const osg::Image> _image;
osg::ref_ptr<const osg::Texture2D> _texture;
osg::Matrix computeTextureMatrix() const;
protected:
virtual ~Source() {}
};
typedef std::vector< osg::ref_ptr<Source> > SourceList;
class Atlas : public osg::Referenced
{
public:
Atlas(unsigned int width, unsigned height, unsigned margin):
_maximumAtlasWidth(width),
_maximumAtlasHeight(height),
_margin(margin),
_x(0),
_width(0),
_height(0){}
unsigned int _maximumAtlasWidth;
unsigned int _maximumAtlasHeight;
unsigned int _margin;
osg::ref_ptr<osg::Texture2D> _texture;
osg::ref_ptr<osg::Image> _image;
SourceList _sourceList;
unsigned int _x;
unsigned int _y;
unsigned int _width;
unsigned int _height;
bool doesSourceFit(Source* source);
bool addSource(Source* source);
void clampToNearestPowerOfTwoSize();
void copySources();
protected:
virtual ~Atlas() {}
};
typedef std::vector< osg::ref_ptr<Atlas> > AtlasList;
Source* getSource(const osg::Image* image);
Source* getSource(const osg::Texture2D* texture);
SourceList _sourceList;
AtlasList _atasList;
};
};

View File

@ -2949,3 +2949,373 @@ void Optimizer::FlattenBillboardVisitor::process()
}
////////////////////////////////////////////////////////////////////////////
// TextureAtlasBuilder
////////////////////////////////////////////////////////////////////////////
Optimizer::TextureAtlasBuilder::TextureAtlasBuilder():
_maximumAtlasWidth(2048),
_maximumAtlasHeight(2048),
_margin(16)
{
}
void Optimizer::TextureAtlasBuilder::setMaximumAtlasSize(unsigned int width, unsigned int height)
{
_maximumAtlasWidth = width;
_maximumAtlasHeight = height;
}
void Optimizer::TextureAtlasBuilder::setMargin(unsigned int margin)
{
_margin = margin;
}
void Optimizer::TextureAtlasBuilder::addSource(const osg::Image* image)
{
if (!getSource(image)) _sourceList.push_back(new Source(image));
}
void Optimizer::TextureAtlasBuilder::addSource(const osg::Texture2D* texture)
{
if (!getSource(texture)) _sourceList.push_back(new Source(texture));
}
void Optimizer::TextureAtlasBuilder::buildAtlas()
{
}
osg::Image* Optimizer::TextureAtlasBuilder::getImageAtlas(unsigned int i)
{
Source* source = _sourceList[i].get();
Atlas* atlas = source ? source->_atlas : 0;
return atlas ? atlas->_image.get() : 0;
}
osg::Texture2D* Optimizer::TextureAtlasBuilder::getTextureAtlas(unsigned int i)
{
Source* source = _sourceList[i].get();
Atlas* atlas = source ? source->_atlas : 0;
return atlas ? atlas->_texture.get() : 0;
}
osg::Matrix Optimizer::TextureAtlasBuilder::getTextureMatrix(unsigned int i)
{
Source* source = _sourceList[i].get();
return source ? source->computeTextureMatrix() : osg::Matrix();
}
osg::Image* Optimizer::TextureAtlasBuilder::getImageAtlas(const osg::Image* image)
{
Source* source = getSource(image);
Atlas* atlas = source ? source->_atlas : 0;
return atlas ? atlas->_image.get() : 0;
}
osg::Texture2D* Optimizer::TextureAtlasBuilder::getTextureAtlas(const osg::Image* image)
{
Source* source = getSource(image);
Atlas* atlas = source ? source->_atlas : 0;
return atlas ? atlas->_texture.get() : 0;
}
osg::Matrix Optimizer::TextureAtlasBuilder::getTextureMatrix(const osg::Image* image)
{
Source* source = getSource(image);
return source ? source->computeTextureMatrix() : osg::Matrix();
}
osg::Image* Optimizer::TextureAtlasBuilder::getImageAtlas(const osg::Texture2D* texture)
{
Source* source = getSource(texture);
Atlas* atlas = source ? source->_atlas : 0;
return atlas ? atlas->_image.get() : 0;
}
osg::Texture2D* Optimizer::TextureAtlasBuilder::getTextureAtlas(const osg::Texture2D* texture)
{
Source* source = getSource(texture);
Atlas* atlas = source ? source->_atlas : 0;
return atlas ? atlas->_texture.get() : 0;
}
osg::Matrix Optimizer::TextureAtlasBuilder::getTextureMatrix(const osg::Texture2D* texture)
{
Source* source = getSource(texture);
return source ? source->computeTextureMatrix() : osg::Matrix();
}
Optimizer::TextureAtlasBuilder::Source* Optimizer::TextureAtlasBuilder::getSource(const osg::Image* image)
{
for(SourceList::iterator itr = _sourceList.begin();
itr != _sourceList.end();
++itr)
{
if ((*itr)->_image == image) return itr->get();
}
return 0;
}
Optimizer::TextureAtlasBuilder::Source* Optimizer::TextureAtlasBuilder::getSource(const osg::Texture2D* texture)
{
for(SourceList::iterator itr = _sourceList.begin();
itr != _sourceList.end();
++itr)
{
if ((*itr)->_texture == texture) return itr->get();
}
return 0;
}
osg::Matrix Optimizer::TextureAtlasBuilder::Source::computeTextureMatrix() const
{
return osg::Matrix();
}
bool Optimizer::TextureAtlasBuilder::Atlas::doesSourceFit(Source* source)
{
// does the source have a valid image?
const osg::Image* sourceImage = source->_image.get();
if (!sourceImage) return false;
// does pixel format match?
if (_image.valid())
{
if (_image->getPixelFormat() != sourceImage->getPixelFormat()) return false;
if (_image->getDataType() != sourceImage->getDataType()) return false;
}
const osg::Texture2D* sourceTexture = source->_texture.get();
if (sourceTexture)
{
if (sourceTexture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::REPEAT ||
sourceTexture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::MIRROR)
{
// can't support repeating textures in texture atlas
return false;
}
if (sourceTexture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::REPEAT ||
sourceTexture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::MIRROR)
{
// can't support repeating textures in texture atlas
return false;
}
if (_texture.valid())
{
bool sourceUsesBorder = sourceTexture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::CLAMP_TO_BORDER ||
sourceTexture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::CLAMP_TO_BORDER;
bool atlasUsesBorder = sourceTexture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::CLAMP_TO_BORDER ||
sourceTexture->getWrap(osg::Texture2D::WRAP_T)==osg::Texture2D::CLAMP_TO_BORDER;
if (sourceUsesBorder!=atlasUsesBorder)
{
// border wrapping does not match
return false;
}
if (sourceUsesBorder)
{
// border colours don't match
if (_texture->getBorderColor() != sourceTexture->getBorderColor()) return false;
}
if (_texture->getFilter(osg::Texture2D::MIN_FILTER) != sourceTexture->getFilter(osg::Texture2D::MIN_FILTER))
{
// inconsitent min filters
return false;
}
if (_texture->getFilter(osg::Texture2D::MAG_FILTER) != sourceTexture->getFilter(osg::Texture2D::MAG_FILTER))
{
// inconsitent mag filters
return false;
}
if (_texture->getMaxAnisotropy() != sourceTexture->getMaxAnisotropy())
{
// anisotropy different.
return false;
}
if (_texture->getInternalFormat() != sourceTexture->getInternalFormat())
{
// internal formats inconistent
return false;
}
if (_texture->getShadowCompareFunc() != sourceTexture->getShadowCompareFunc())
{
// shadow functions inconsitent
return false;
}
if (_texture->getShadowTextureMode() != sourceTexture->getShadowTextureMode())
{
// shadow texture mode inconsitent
return false;
}
if (_texture->getShadowAmbient() != sourceTexture->getShadowAmbient())
{
// shadow ambient inconsitent
return false;
}
if (sourceTexture->getReadPBuffer()!=0)
{
// pbuffer textures not suitable
return false;
}
}
}
if (sourceImage->s() + 2*_margin > _maximumAtlasWidth)
{
// image too big for Atlas
return false;
}
if (sourceImage->t() + 2*_margin > _maximumAtlasHeight)
{
// image too big for Atlas
return false;
}
if ((_y + sourceImage->t() + 2*_margin) > _maximumAtlasWidth)
{
// image doesn't have up space in height axis.
}
// does the source fit in the current row?
if ((_x + sourceImage->s() + 2*_margin) <= _maximumAtlasWidth)
{
// yes it fits :-)
return true;
}
// does the source fit in the new row up?
if ((_height + sourceImage->t() + 2*_margin) <= _maximumAtlasHeight)
{
// yes it fits :-)
return true;
}
// no space for the texture
return false;
}
bool Optimizer::TextureAtlasBuilder::Atlas::addSource(Source* source)
{
// double check source is compatible
if (!doesSourceFit(source)) return false;
const osg::Image* sourceImage = source->_image.get();
const osg::Texture2D* sourceTexture = source->_texture.get();
if (!_image)
{
// need to create an image of the same pixel format to store the atlas in
_image = new osg::Image;
_image->setPixelFormat(sourceImage->getPixelFormat());
_image->setDataType(sourceImage->getDataType());
}
if (!_texture && sourceTexture)
{
_texture = new osg::Texture2D(_image.get());
_texture->setWrap(osg::Texture2D::WRAP_S, sourceTexture->getWrap(osg::Texture2D::WRAP_S));
_texture->setWrap(osg::Texture2D::WRAP_T, sourceTexture->getWrap(osg::Texture2D::WRAP_T));
_texture->setBorderColor(sourceTexture->getBorderColor());
_texture->setBorderWidth(0);
_texture->setFilter(osg::Texture2D::MIN_FILTER, sourceTexture->getFilter(osg::Texture2D::MIN_FILTER));
_texture->setFilter(osg::Texture2D::MAG_FILTER, sourceTexture->getFilter(osg::Texture2D::MAG_FILTER));
_texture->setMaxAnisotropy(sourceTexture->getMaxAnisotropy());
_texture->setInternalFormat(sourceTexture->getInternalFormat());
_texture->setShadowCompareFunc(sourceTexture->getShadowCompareFunc());
_texture->setShadowTextureMode(sourceTexture->getShadowTextureMode());
_texture->setShadowAmbient(sourceTexture->getShadowAmbient());
}
// now work out where to fit it, first try current row.
if ((_x + sourceImage->s() + 2*_margin) <= _maximumAtlasWidth)
{
// yes it fits, so add the source to the atlas's list of sources it contains
_sourceList.push_back(source);
// set up the source so it knows where it is in the atlas
source->_x = _x + _margin;
source->_y = _y + _margin;
source->_atlas = this;
// move the atlas' cursor along to the right
_x += sourceImage->s() + 2*_margin;
if (_x > _width) _width = _x;
return true;
}
// does the source fit in the new row up?
if ((_height + sourceImage->t() + 2*_margin) <= _maximumAtlasHeight)
{
// now row so first need to reset the atlas cursor
_x = 0;
_y = _height;
// yes it fits, so add the source to the atlas' list of sources it contains
_sourceList.push_back(source);
// set up the source so it knows where it is in the atlas
source->_x = _x + _margin;
source->_y = _y + _margin;
source->_atlas = this;
// move the atlas' cursor along to the right
_x += sourceImage->s() + 2*_margin;
return true;
}
// shouldn't get here, unless doesSourceFit isn't working...
return false;
}
void Optimizer::TextureAtlasBuilder::Atlas::clampToNearestPowerOfTwoSize()
{
unsigned int w = 1;
while (w<_width) w *= 2;
unsigned int h = 1;
while (h<_height) h *= 2;
osg::notify(osg::NOTICE)<<"Clamping "<<_width<<", "<<_height<<" to "<<w<<","<<h<<std::endl;
_width = w;
_height = w;
}
void Optimizer::TextureAtlasBuilder::Atlas::copySources()
{
osg::notify(osg::NOTICE)<<"Atlas::copySources() "<<std::endl;
for(SourceList::iterator itr = _sourceList.begin();
itr !=_sourceList.end();
++itr)
{
osg::notify(osg::NOTICE)<<"Copying image "<<itr->_image->getFileName()<<" to "<<itr->_x<<" ,"<<itr->_y<<std::endl;
}
}