From 147fdd0430a6dd791da31b8cd52ee9df6fff9dcd Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Fri, 5 Nov 2010 17:04:08 +0000 Subject: [PATCH] From Sukender, "1. More handled cases in MergeGeometryVisitor - Algorithm doesn't try to merge double and single precision arrays together - Algorithm doesn't try to merge incompatible geometries (ex: one with "vertices + texoords", and another with only vertices) 2. Better TextureAtlasBuilder Algorithm is still sub-optimal, but it now tries to fill more blanks, using "unused space in the current line". (Don't know if I already submitted it, but I guess not) One day, someone should try to find a good solution to this NP-problem... For instance : http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.140.200&rep=rep1&type=pdf " --- include/osgUtil/Optimizer | 73 +++--- src/osgUtil/Optimizer.cpp | 497 +++++++++++++++++++++++++++++--------- 2 files changed, 423 insertions(+), 147 deletions(-) diff --git a/include/osgUtil/Optimizer b/include/osgUtil/Optimizer index dfbde9496..858405426 100644 --- a/include/osgUtil/Optimizer +++ b/include/osgUtil/Optimizer @@ -663,13 +663,13 @@ class OSGUTIL_EXPORT Optimizer void reset(); - void setMaximumAtlasSize(unsigned int width, unsigned int height); + void setMaximumAtlasSize(int width, int height); - unsigned int getMaximumAtlasWidth() const { return _maximumAtlasWidth; } - unsigned int getMaximumAtlasHeight() const { return _maximumAtlasHeight; } + int getMaximumAtlasWidth() const { return _maximumAtlasWidth; } + int getMaximumAtlasHeight() const { return _maximumAtlasHeight; } - void setMargin(unsigned int margin); - unsigned int getMargin() const { return _margin; } + void setMargin(int margin); + int getMargin() const { return _margin; } void addSource(const osg::Image* image); void addSource(const osg::Texture2D* texture); @@ -677,9 +677,8 @@ class OSGUTIL_EXPORT Optimizer 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); @@ -694,14 +693,14 @@ class OSGUTIL_EXPORT Optimizer protected: - unsigned int _maximumAtlasWidth; - unsigned int _maximumAtlasHeight; - unsigned int _margin; + int _maximumAtlasWidth; + int _maximumAtlasHeight; + int _margin; // forward declare class Atlas; - + class Source : public osg::Referenced { public: @@ -714,14 +713,14 @@ class OSGUTIL_EXPORT Optimizer Source(const osg::Texture2D* texture): _x(0),_y(0),_atlas(0),_texture(texture) { if (texture) _image = texture->getImage(); } - unsigned int _x; - unsigned int _y; + int _x; + int _y; Atlas* _atlas; osg::ref_ptr _image; osg::ref_ptr _texture; - bool suitableForAtlas(unsigned int maximumAtlasWidth, unsigned int maximumAtlasHeight, unsigned int margin); + bool suitableForAtlas(int maximumAtlasWidth, int maximumAtlasHeight, int margin); osg::Matrix computeTextureMatrix() const; @@ -729,42 +728,51 @@ class OSGUTIL_EXPORT Optimizer virtual ~Source() {} }; - + typedef std::vector< osg::ref_ptr > SourceList; class Atlas : public osg::Referenced { public: - Atlas(unsigned int width, unsigned height, unsigned margin): + Atlas(int width, int height, int margin): _maximumAtlasWidth(width), _maximumAtlasHeight(height), _margin(margin), _x(0), _y(0), _width(0), - _height(0){} + _height(0), + _indexFirstOfRow(0){} - unsigned int _maximumAtlasWidth; - unsigned int _maximumAtlasHeight; - unsigned int _margin; + int _maximumAtlasWidth; + int _maximumAtlasHeight; + int _margin; osg::ref_ptr _texture; osg::ref_ptr _image; SourceList _sourceList; - unsigned int _x; - unsigned int _y; - unsigned int _width; - unsigned int _height; - - bool doesSourceFit(Source* source); + int _x; + int _y; + int _width; + int _height; + unsigned int _indexFirstOfRow; ///< Contain the index of the first element of the last row. + enum FitsIn + { + NO, + YES, + IN_NEXT_ROW + }; + //Return YES if it fits in the current row, + //NO if it can't fit in any row. + //IN_NEXT_ROW if it can't fin the the current row, but can fit in next row or atlas. + FitsIn doesSourceFit(Source* source); bool addSource(Source* source); void clampToNearestPowerOfTwoSize(); void copySources(); protected: - virtual ~Atlas() {} }; @@ -774,7 +782,16 @@ class OSGUTIL_EXPORT Optimizer Source* getSource(const osg::Texture2D* texture); SourceList _sourceList; - AtlasList _atlasList; + AtlasList _atlasList; + private: + struct CompareSrc + { + bool operator()(osg::ref_ptr src1, osg::ref_ptr src2) const + { + return src1->_image->t() > src2->_image->t(); + } + }; + void completeRow(unsigned int indexAtlas); }; diff --git a/src/osgUtil/Optimizer.cpp b/src/osgUtil/Optimizer.cpp index 560b77b46..68232cdfc 100644 --- a/src/osgUtil/Optimizer.cpp +++ b/src/osgUtil/Optimizer.cpp @@ -1261,7 +1261,7 @@ bool Optimizer::FlattenStaticTransformsVisitor::removeTransforms(osg::Node* node } //////////////////////////////////////////////////////////////////////////// -// CombineStatucTransforms +// CombineStaticTransforms //////////////////////////////////////////////////////////////////////////// void Optimizer::CombineStaticTransformsVisitor::apply(osg::MatrixTransform& transform) @@ -1851,6 +1851,49 @@ void Optimizer::MakeFastGeometryVisitor::checkGeode(osg::Geode& geode) } } +/// Shortcut to get size of an array, even if pointer is NULL. +inline unsigned int getSize(const osg::Array * a) { return a ? a->getNumElements() : 0; } + +/// When merging geometries, tests if two arrays can be merged, regarding to their number of components, and the number of vertices. +bool isArrayCompatible(unsigned int numVertice1, unsigned int numVertice2, const osg::Array* compare1, const osg::Array* compare2) +{ + // Sumed up truth table: + // If array (1 or 2) not empty and vertices empty => error, should not happen (allows simplification in formulae below) + // If one side has both vertices and array, and the other side has only vertices => then arrays cannot be merged + // Else, arrays can be merged + //assert(numVertice1 || !getSize(compare1)); + //assert(numVertice2 || !getSize(compare2)); + return !( (numVertice1 && !getSize(compare1) && getSize(compare2)) + || (numVertice2 && !getSize(compare2) && getSize(compare1)) ); +} + +/// Return true only if both geometries have same array type and if arrays (such as TexCoords) are compatible (i.e. both empty or both filled) +bool isAbleToMerge(const osg::Geometry& g1, const osg::Geometry& g2) +{ + unsigned int numVertice1( getSize(g1.getVertexArray()) ); + unsigned int numVertice2( getSize(g2.getVertexArray()) ); + + // first verify arrays size + if (!isArrayCompatible(numVertice1,numVertice2,g1.getNormalArray(),g2.getNormalArray()) || + !isArrayCompatible(numVertice1,numVertice2,g1.getColorArray(),g2.getColorArray()) || + !isArrayCompatible(numVertice1,numVertice2,g1.getSecondaryColorArray(),g2.getSecondaryColorArray()) || + !isArrayCompatible(numVertice1,numVertice2,g1.getFogCoordArray(),g2.getFogCoordArray()) || + g1.getNumTexCoordArrays()!=g2.getNumTexCoordArrays()) return false; + + for (unsigned int eachTexCoordArray=0;eachTexCoordArraygetDataType()!=g2.getVertexArray()->getDataType()) return false; + if (g1.getNormalArray() && g2.getNormalArray() && g1.getNormalArray()->getDataType()!=g2.getNormalArray()->getDataType()) return false; + if (g1.getColorArray() && g2.getColorArray() && g1.getColorArray()->getDataType()!=g2.getColorArray()->getDataType()) return false; + if (g1.getSecondaryColorArray() && g2.getSecondaryColorArray() && g1.getSecondaryColorArray()->getDataType()!=g2.getSecondaryColorArray()->getDataType()) return false; + if (g1.getFogCoordArray() && g2.getNormalArray() && g1.getFogCoordArray()->getDataType()!=g2.getFogCoordArray()->getDataType()) return false; + return true; +} + bool Optimizer::MergeGeometryVisitor::mergeGeode(osg::Geode& geode) { if (!isOperationPermissibleForObject(&geode)) return false; @@ -1894,52 +1937,116 @@ bool Optimizer::MergeGeometryVisitor::mergeGeode(osg::Geode& geode) } #if 1 - bool needToDoMerge = false; - MergeList mergeList; + // first try to group geometries with the same properties + // (i.e. array types) to avoid loss of data during merging + MergeList mergeListChecked; // List of drawables just before merging, grouped by "compatibility" and vertex limit + MergeList mergeList; // Intermediate list of drawables, grouped ony by "compatibility" for(GeometryDuplicateMap::iterator itr=geometryDuplicateMap.begin(); itr!=geometryDuplicateMap.end(); ++itr) { - mergeList.push_back(DuplicateList()); - DuplicateList* duplicateList = &mergeList.back(); - - if (itr->second.size()>1) + if (itr->second.empty()) continue; + if (itr->second.size()==1) { - std::sort(itr->second.begin(),itr->second.end(),LessGeometryPrimitiveType()); - osg::Geometry* lhs = itr->second[0]; + mergeList.push_back(DuplicateList()); + DuplicateList* duplicateList = &mergeList.back(); + duplicateList->push_back(itr->second[0]); + continue; + } - duplicateList->push_back(lhs); - - unsigned int numVertices = lhs->getVertexArray() ? lhs->getVertexArray()->getNumElements() : 0; + std::sort(itr->second.begin(),itr->second.end(),LessGeometryPrimitiveType()); - for(DuplicateList::iterator dupItr=itr->second.begin()+1; - dupItr!=itr->second.end(); - ++dupItr) + // initialize the temporary list by pushing the first geometry + MergeList mergeListTmp; + mergeListTmp.push_back(DuplicateList()); + DuplicateList* duplicateList = &mergeListTmp.back(); + duplicateList->push_back(itr->second[0]); + + for(DuplicateList::iterator dupItr=itr->second.begin()+1; + dupItr!=itr->second.end(); + ++dupItr) + { + osg::Geometry* geomToPush = *dupItr; + + // try to group geomToPush with another geometry + MergeList::iterator eachMergeList=mergeListTmp.begin(); + for(;eachMergeList!=mergeListTmp.end();++eachMergeList) { - - osg::Geometry* rhs = *dupItr; - - unsigned int numRhsVertices = rhs->getVertexArray() ? rhs->getVertexArray()->getNumElements() : 0; - - if (numVertices+numRhsVertices < _targetMaximumNumberOfVertices) + if (!eachMergeList->empty() && eachMergeList->front()!=NULL + && isAbleToMerge(*eachMergeList->front(),*geomToPush)) { - duplicateList->push_back(rhs); - numVertices += numRhsVertices; - needToDoMerge = true; - } - else - { - numVertices = numRhsVertices; - mergeList.push_back(DuplicateList()); - duplicateList = &mergeList.back(); - duplicateList->push_back(rhs); + eachMergeList->push_back(geomToPush); + break; } + } + // if no suitable group was found, then a new one is created + if (eachMergeList==mergeListTmp.end()) + { + mergeListTmp.push_back(DuplicateList()); + duplicateList = &mergeListTmp.back(); + duplicateList->push_back(geomToPush); } } - else if (itr->second.size()>0) + + // copy the group in the mergeListChecked + for(MergeList::iterator eachMergeList=mergeListTmp.begin();eachMergeList!=mergeListTmp.end();++eachMergeList) { - duplicateList->push_back(itr->second[0]); + mergeListChecked.push_back(*eachMergeList); + } + } + + // then build merge list using _targetMaximumNumberOfVertices + bool needToDoMerge = false; + // dequeue each DuplicateList when vertices limit is reached or when all elements has been checked + for(;!mergeListChecked.empty();) + { + MergeList::iterator itr=mergeListChecked.begin(); + DuplicateList& duplicateList(*itr); + if (duplicateList.size()==0) + { + mergeListChecked.erase(itr); + continue; + } + + if (duplicateList.size()==1) + { + mergeList.push_back(duplicateList); + mergeListChecked.erase(itr); + continue; + } + + unsigned int numVertices(duplicateList.front()->getVertexArray() ? duplicateList.front()->getVertexArray()->getNumElements() : 0); + DuplicateList::iterator eachGeom(duplicateList.begin()+1); + // until all geometries have been checked or _targetMaximumNumberOfVertices is reached + for (;eachGeom!=duplicateList.end(); ++eachGeom) + { + unsigned int numAddVertices((*eachGeom)->getVertexArray() ? (*eachGeom)->getVertexArray()->getNumElements() : 0); + if (numVertices+numAddVertices<_targetMaximumNumberOfVertices) + { + break; + } + else + { + numVertices += numAddVertices; + } + } + + // push back if bellow the limit + if (numVertices<_targetMaximumNumberOfVertices) + { + if (duplicateList.size()>1) needToDoMerge = true; + mergeList.push_back(duplicateList); + mergeListChecked.erase(itr); + } + // else split the list to store what is below the limit and retry on what is above + else + { + mergeList.push_back(DuplicateList()); + DuplicateList* duplicateListResult = &mergeList.back(); + duplicateListResult->insert(duplicateListResult->end(),duplicateList.begin(),eachGeom); + duplicateList.erase(duplicateList.begin(),eachGeom); + if (duplicateListResult->size()>1) needToDoMerge = true; } } @@ -2272,25 +2379,26 @@ bool Optimizer::MergeGeometryVisitor::geometryContainsSharedArrays(osg::Geometry class MergeArrayVisitor : public osg::ArrayVisitor { - public: - + protected: osg::Array* _lhs; int _offset; - + public: MergeArrayVisitor() : _lhs(0), _offset(0) {} - void merge(osg::Array* lhs,osg::Array* rhs, int offset=0) + /// try to merge the content of two arrays. + bool merge(osg::Array* lhs,osg::Array* rhs, int offset=0) { - if (lhs==0 || rhs==0) return; - if (lhs->getType()!=rhs->getType()) return; + if (lhs==0 || rhs==0) return true; + if (lhs->getType()!=rhs->getType()) return false; _lhs = lhs; _offset = offset; rhs->accept(*this); + return true; } template @@ -2353,8 +2461,10 @@ bool Optimizer::MergeGeometryVisitor::mergeGeometry(osg::Geometry& lhs,osg::Geom { base = lhs.getVertexArray()->getNumElements(); - merger.merge(lhs.getVertexArray(),rhs.getVertexArray()); - + if (!merger.merge(lhs.getVertexArray(),rhs.getVertexArray())) + { + OSG_DEBUG << "MergeGeometry: vertex array not merged. Some data may be lost." <getNumElements(); - merger.merge(lhs.getVertexIndices(),rhs.getVertexIndices(),vbase); - + if (!merger.merge(lhs.getVertexIndices(),rhs.getVertexIndices(),vbase)) + { + OSG_DEBUG << "MergeGeometry: vertex indices not merged. Some data may be lost." <getNumElements() : 0; if (lhs.getNormalArray() && rhs.getNormalArray() && lhs.getNormalBinding()!=osg::Geometry::BIND_OVERALL) { - merger.merge(lhs.getNormalArray(),rhs.getNormalArray()); + if (!merger.merge(lhs.getNormalArray(),rhs.getNormalArray())) + { + OSG_DEBUG << "MergeGeometry: normal array not merged. Some data may be lost." <getNumElements() : 0; if (lhs.getColorArray() && rhs.getColorArray() && lhs.getColorBinding()!=osg::Geometry::BIND_OVERALL) { - merger.merge(lhs.getColorArray(),rhs.getColorArray()); + if (!merger.merge(lhs.getColorArray(),rhs.getColorArray())) + { + OSG_DEBUG << "MergeGeometry: color array not merged. Some data may be lost." <getNumElements() : 0; if (lhs.getSecondaryColorArray() && rhs.getSecondaryColorArray() && lhs.getSecondaryColorBinding()!=osg::Geometry::BIND_OVERALL) { - merger.merge(lhs.getSecondaryColorArray(),rhs.getSecondaryColorArray()); + if (!merger.merge(lhs.getSecondaryColorArray(),rhs.getSecondaryColorArray())) + { + OSG_DEBUG << "MergeGeometry: secondary color array not merged. Some data may be lost." <getNumElements() : 0; if (lhs.getFogCoordArray() && rhs.getFogCoordArray() && lhs.getFogCoordBinding()!=osg::Geometry::BIND_OVERALL) { - merger.merge(lhs.getFogCoordArray(),rhs.getFogCoordArray()); + if (!merger.merge(lhs.getFogCoordArray(),rhs.getFogCoordArray())) + { + OSG_DEBUG << "MergeGeometry: fog coord array not merged. Some data may be lost." <getNumElements() : 0; - merger.merge(lhs.getTexCoordArray(unit),rhs.getTexCoordArray(unit)); - + if (!merger.merge(lhs.getTexCoordArray(unit),rhs.getTexCoordArray(unit))) + { + OSG_DEBUG << "MergeGeometry: tex coord array not merged. Some data may be lost." <getNumElements() : 0; - merger.merge(lhs.getVertexAttribArray(unit),rhs.getVertexAttribArray(unit)); + if (!merger.merge(lhs.getVertexAttribArray(unit),rhs.getVertexAttribArray(unit))) + { + OSG_DEBUG << "MergeGeometry: vertex attrib array not merged. Some data may be lost." <get(); + if(atlas->_indexFirstOfRow < atlas->_sourceList.size()) + { + //Try to fill the row with smaller images. + int x_max = atlas->_width - _margin; + int y_max = atlas->_height - _margin; + //int x_max = atlas->_maximumAtlasWidth - _margin; + //int y_max = atlas->_maximumAtlasHeight - _margin; + + // Fill last Row + for(SourceList::iterator sitr3 = _sourceList.begin(); sitr3 != _sourceList.end(); ++sitr3) + { + int x_min = atlas->_x + _margin; + int y_min = atlas->_y + _margin; + if (y_min >= y_max || x_min >= x_max) continue; + + Source * source = sitr3->get(); + if (source->_atlas || atlas->_image->getPixelFormat() != source->_image->getPixelFormat() || + atlas->_image->getDataType() != source->_image->getDataType()) + { + continue; + } + + int image_s = source->_image->s(); + int image_t = source->_image->t(); + if (x_min + image_s <= x_max && y_min + image_t <= y_max) // Test if the image can fit in the empty space. + { + source->_x = x_min; + source->_y = y_min; + //assert(source->_x + source->_image->s()+_margin <= atlas->_maximumAtlasWidth ); // "+_margin" and not "+2*_margin" because _x already takes the margin into account + //assert(source->_y + source->_image->t()+_margin <= atlas->_maximumAtlasHeight); + //assert(source->_x >= _margin); + //assert(source->_y >= _margin); + atlas->_x += image_s + 2*_margin; + //assert(atlas->_x <= atlas->_maximumAtlasWidth); + source->_atlas = atlas; + atlas->_sourceList.push_back(source); + } + } + + // Fill the last column + SourceList srcListTmp; + for(SourceList::iterator sitr4 = atlas->_sourceList.begin() + atlas->_indexFirstOfRow; + sitr4 != atlas->_sourceList.end(); ++sitr4) + { + Source * srcAdded = sitr4->get(); + int y_min = srcAdded->_y + srcAdded->_image->t() + 2 * _margin; + int x_min = srcAdded->_x; + int x_max = x_min + srcAdded->_image->s(); // Hides upper block's x_max + if (y_min >= y_max || x_min >= x_max) continue; + + Source * maxWidthSource = NULL; + for(SourceList::iterator sitr2 = _sourceList.begin(); sitr2 != _sourceList.end(); ++sitr2) + { + Source * source = sitr2->get(); + if (source->_atlas || atlas->_image->getPixelFormat() != source->_image->getPixelFormat() || + atlas->_image->getDataType() != source->_image->getDataType()) + { + continue; + } + int image_s = source->_image->s(); + int image_t = source->_image->t(); + if(x_min + image_s <= x_max && y_min + image_t <= y_max) // Test if the image can fit in the empty space. + { + if (maxWidthSource == NULL || maxWidthSource->_image->s() < source->_image->s()) + { + maxWidthSource = source; //Keep the maximum width for source. + } + } + } + if (maxWidthSource) + { + // Add the source with the max width to the atlas + maxWidthSource->_x = x_min; + maxWidthSource->_y = y_min; + maxWidthSource->_atlas = atlas; + srcListTmp.push_back(maxWidthSource); //Store the mawWidth source in the temporary vector. + } + } + for(SourceList::iterator itTmp = srcListTmp.begin(); itTmp != srcListTmp.end(); ++itTmp) + { + //Add the sources to the general list (wasn't possible in the loop using the iterator on the same list) + atlas->_sourceList.push_back(*itTmp); + } + atlas->_indexFirstOfRow = atlas->_sourceList.size(); + } +} + void Optimizer::TextureAtlasBuilder::buildAtlas() { - // assign the source to the atlas + std::sort(_sourceList.begin(), _sourceList.end(), CompareSrc()); // Sort using the height of images _atlasList.clear(); for(SourceList::iterator sitr = _sourceList.begin(); sitr != _sourceList.end(); ++sitr) { - Source* source = sitr->get(); - if (source->suitableForAtlas(_maximumAtlasWidth,_maximumAtlasHeight,_margin)) + Source * source = sitr->get(); + if (!source->_atlas && source->suitableForAtlas(_maximumAtlasWidth,_maximumAtlasHeight,_margin)) { bool addedSourceToAtlas = false; for(AtlasList::iterator aitr = _atlasList.begin(); aitr != _atlasList.end() && !addedSourceToAtlas; ++aitr) { - OSG_INFO<<"checking source "<_image->getFileName()<<" to see it it'll fit in atlas "<get()<doesSourceFit(source)) + if(!(*aitr)->_image || + ((*aitr)->_image->getPixelFormat() == (*sitr)->_image->getPixelFormat() && + (*aitr)->_image->getPacking() == (*sitr)->_image->getPacking())) { - addedSourceToAtlas = true; - (*aitr)->addSource(source); - } - else - { - OSG_INFO<<"source "<_image->getFileName()<<" does not fit in atlas "<get()<_image->getFileName()<<" to see it it'll fit in atlas "<get()<doesSourceFit(source); + if (fitsIn == Optimizer::TextureAtlasBuilder::Atlas::YES) + { + addedSourceToAtlas = true; + (*aitr)->addSource(source); // Add in the currentRow. + } + else if(fitsIn == Optimizer::TextureAtlasBuilder::Atlas::IN_NEXT_ROW) + { + completeRow(aitr - _atlasList.begin()); //Fill Empty spaces. + addedSourceToAtlas = true; + (*aitr)->addSource(source); // Add the source in the new row. + } + else + { + completeRow(aitr - _atlasList.begin()); //Fill Empty spaces before creating a new atlas. + } } } @@ -3295,9 +3548,8 @@ void Optimizer::TextureAtlasBuilder::buildAtlas() OSG_INFO<<"creating new Atlas for "<_image->getFileName()< atlas = new Atlas(_maximumAtlasWidth,_maximumAtlasHeight,_margin); - _atlasList.push_back(atlas.get()); - - atlas->addSource(source); + _atlasList.push_back(atlas); + if (!source->_atlas) atlas->addSource(source); } } } @@ -3308,13 +3560,13 @@ void Optimizer::TextureAtlasBuilder::buildAtlas() aitr != _atlasList.end(); ++aitr) { - Atlas* atlas = aitr->get(); + osg::ref_ptr atlas = *aitr; if (atlas->_sourceList.size()==1) { // no point building an atlas with only one entry // so disconnect the source. - Source* source = atlas->_sourceList[0].get(); + Source * source = atlas->_sourceList[0].get(); source->_atlas = 0; atlas->_sourceList.clear(); } @@ -3324,14 +3576,11 @@ void Optimizer::TextureAtlasBuilder::buildAtlas() std::stringstream ostr; ostr<<"atlas_"<_image->setFileName(ostr.str()); - activeAtlasList.push_back(atlas); atlas->clampToNearestPowerOfTwoSize(); atlas->copySources(); - } } - // keep only the active atlas' _atlasList.swap(activeAtlasList); @@ -3419,7 +3668,7 @@ Optimizer::TextureAtlasBuilder::Source* Optimizer::TextureAtlasBuilder::getSourc return 0; } -bool Optimizer::TextureAtlasBuilder::Source::suitableForAtlas(unsigned int maximumAtlasWidth, unsigned int maximumAtlasHeight, unsigned int margin) +bool Optimizer::TextureAtlasBuilder::Source::suitableForAtlas(int maximumAtlasWidth, int maximumAtlasHeight, int margin) { if (!_image) return false; @@ -3450,7 +3699,6 @@ bool Optimizer::TextureAtlasBuilder::Source::suitableForAtlas(unsigned int maxim // pixel size not byte aligned so report as not suitable to prevent other atlas code from having problems with byte boundaries. return false; } - if (_texture.valid()) { @@ -3483,22 +3731,23 @@ osg::Matrix Optimizer::TextureAtlasBuilder::Source::computeTextureMatrix() const if (!_atlas) return osg::Matrix(); if (!_image) return osg::Matrix(); if (!(_atlas->_image)) return osg::Matrix(); - - return osg::Matrix::scale(float(_image->s())/float(_atlas->_image->s()), float(_image->t())/float(_atlas->_image->t()), 1.0)* - osg::Matrix::translate(float(_x)/float(_atlas->_image->s()), float(_y)/float(_atlas->_image->t()), 0.0); + + typedef osg::Matrix::value_type Float; + return osg::Matrix::scale(Float(_image->s())/Float(_atlas->_image->s()), Float(_image->t())/Float(_atlas->_image->t()), 1.0)* + osg::Matrix::translate(Float(_x)/Float(_atlas->_image->s()), Float(_y)/Float(_atlas->_image->t()), 0.0); } -bool Optimizer::TextureAtlasBuilder::Atlas::doesSourceFit(Source* source) +Optimizer::TextureAtlasBuilder::Atlas::FitsIn Optimizer::TextureAtlasBuilder::Atlas::doesSourceFit(Source* source) { // does the source have a valid image? const osg::Image* sourceImage = source->_image.get(); - if (!sourceImage) return false; + if (!sourceImage) return NO; // does pixel format match? if (_image.valid()) { - if (_image->getPixelFormat() != sourceImage->getPixelFormat()) return false; - if (_image->getDataType() != sourceImage->getDataType()) return false; + if (_image->getPixelFormat() != sourceImage->getPixelFormat()) return NO; + if (_image->getDataType() != sourceImage->getDataType()) return NO; } const osg::Texture2D* sourceTexture = source->_texture.get(); @@ -3508,20 +3757,20 @@ bool Optimizer::TextureAtlasBuilder::Atlas::doesSourceFit(Source* source) sourceTexture->getWrap(osg::Texture2D::WRAP_S)==osg::Texture2D::MIRROR) { // can't support repeating textures in texture atlas - return false; + return NO; } 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; + return NO; } if (sourceTexture->getReadPBuffer()!=0) { // pbuffer textures not suitable - return false; + return NO; } if (_texture.valid()) @@ -3536,55 +3785,55 @@ bool Optimizer::TextureAtlasBuilder::Atlas::doesSourceFit(Source* source) if (sourceUsesBorder!=atlasUsesBorder) { // border wrapping does not match - return false; + return NO; } if (sourceUsesBorder) { // border colours don't match - if (_texture->getBorderColor() != sourceTexture->getBorderColor()) return false; + if (_texture->getBorderColor() != sourceTexture->getBorderColor()) return NO; } if (_texture->getFilter(osg::Texture2D::MIN_FILTER) != sourceTexture->getFilter(osg::Texture2D::MIN_FILTER)) { // inconsitent min filters - return false; + return NO; } if (_texture->getFilter(osg::Texture2D::MAG_FILTER) != sourceTexture->getFilter(osg::Texture2D::MAG_FILTER)) { // inconsitent mag filters - return false; + return NO; } if (_texture->getMaxAnisotropy() != sourceTexture->getMaxAnisotropy()) { // anisotropy different. - return false; + return NO; } if (_texture->getInternalFormat() != sourceTexture->getInternalFormat()) { // internal formats inconistent - return false; + return NO; } if (_texture->getShadowCompareFunc() != sourceTexture->getShadowCompareFunc()) { // shadow functions inconsitent - return false; + return NO; } if (_texture->getShadowTextureMode() != sourceTexture->getShadowTextureMode()) { // shadow texture mode inconsitent - return false; + return NO; } if (_texture->getShadowAmbient() != sourceTexture->getShadowAmbient()) { // shadow ambient inconsitent - return false; + return NO; } } } @@ -3592,19 +3841,19 @@ bool Optimizer::TextureAtlasBuilder::Atlas::doesSourceFit(Source* source) if (sourceImage->s() + 2*_margin > _maximumAtlasWidth) { // image too big for Atlas - return false; + return NO; } if (sourceImage->t() + 2*_margin > _maximumAtlasHeight) { // image too big for Atlas - return false; + return NO; } if ((_y + sourceImage->t() + 2*_margin) > _maximumAtlasHeight) { // image doesn't have up space in height axis. - return false; + return NO; } // does the source fit in the current row? @@ -3612,7 +3861,7 @@ bool Optimizer::TextureAtlasBuilder::Atlas::doesSourceFit(Source* source) { // yes it fits :-) OSG_INFO<<"Fits in current row"<_image->getFileName()<<" does not fit in atlas "<_image.get(); const osg::Texture2D* sourceTexture = source->_texture.get(); @@ -3643,10 +3891,11 @@ bool Optimizer::TextureAtlasBuilder::Atlas::addSource(Source* source) { // need to create an image of the same pixel format to store the atlas in _image = new osg::Image; + _image->setPacking(sourceImage->getPacking()); _image->setPixelFormat(sourceImage->getPixelFormat()); _image->setDataType(sourceImage->getDataType()); } - + if (!_texture && sourceTexture) { _texture = new osg::Texture2D(_image.get()); @@ -3688,7 +3937,7 @@ bool Optimizer::TextureAtlasBuilder::Atlas::addSource(Source* source) if (_x > _width) _width = _x; - unsigned int localTop = _y + sourceImage->t() + 2*_margin; + int localTop = _y + sourceImage->t() + 2*_margin; if ( localTop > _height) _height = localTop; return true; @@ -3731,10 +3980,10 @@ bool Optimizer::TextureAtlasBuilder::Atlas::addSource(Source* source) void Optimizer::TextureAtlasBuilder::Atlas::clampToNearestPowerOfTwoSize() { - unsigned int w = 1; + int w = 1; while (w<_width) w *= 2; - unsigned int h = 1; + int h = 1; while (h<_height) h *= 2; OSG_INFO<<"Clamping "<<_width<<", "<<_height<<" to "<getPixelFormat(); + GLenum dataType = _image->getDataType(); + GLenum packing = _image->getPacking(); OSG_INFO<<"Allocated to "<<_width<<","<<_height<allocateImage(_width,_height,1, - _image->getPixelFormat(),_image->getDataType(), - _image->getPacking()); + pixelFormat, dataType, + packing); { // clear memory @@ -3759,6 +4011,7 @@ void Optimizer::TextureAtlasBuilder::Atlas::copySources() } OSG_INFO<<"Atlas::copySources() "<get(); Atlas* atlas = source->_atlas; - if (atlas) + if (atlas == this) { OSG_INFO<<"Copying image "<_image->getFileName()<<" to "<_x<<" ,"<_y<_image->s()<<","<_image->t()<_image.get(); osg::Image* atlasImage = atlas->_image.get(); - + //assert(sourceImage->getPacking() == atlasImage->getPacking()); //Test if packings are equals. unsigned int rowSize = sourceImage->getRowSizeInBytes(); unsigned int pixelSizeInBits = sourceImage->getPixelSizeInBits(); unsigned int pixelSizeInBytes = pixelSizeInBits/8; unsigned int marginSizeInBytes = pixelSizeInBytes*_margin; - unsigned int x = source->_x; - unsigned int y = source->_y; + //assert(atlas->_width == static_cast(atlasImage->s())); + //assert(atlas->_height == static_cast(atlasImage->t())); + //assert(source->_x + static_cast(source->_image->s())+_margin <= static_cast(atlas->_image->s())); // "+_margin" and not "+2*_margin" because _x already takes the margin into account + //assert(source->_y + static_cast(source->_image->t())+_margin <= static_cast(atlas->_image->t())); + //assert(source->_x >= _margin); + //assert(source->_y >= _margin); + int x = source->_x; + int y = source->_y; int t; for(t=0; tt(); ++t, ++y) @@ -3795,7 +4054,7 @@ void Optimizer::TextureAtlasBuilder::Atlas::copySources() // copy top row margin y = source->_y + sourceImage->t(); - unsigned int m; + int m; for(m=0; m<_margin; ++m, ++y) { unsigned char* destPtr = atlasImage->data(x, y); @@ -4135,7 +4394,8 @@ void Optimizer::TextureAtlasVisitor::optimize() texture->setWrap(osg::Texture2D::WRAP_T, osg::Texture::CLAMP); } } - + //typedef std::list SourceListTmp; + //SourceListTmp sourceToAdd; // add the textures as sources for the TextureAtlasBuilder for(titr = _textures.begin(); titr != _textures.end(); @@ -4154,8 +4414,7 @@ void Optimizer::TextureAtlasVisitor::optimize() _builder.addSource(*titr); } } - - // build the atlas' + _builder.buildAtlas();