From 58c47e984243285c8bf97b5449d5c601827c8988 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Tue, 16 Dec 2014 09:34:15 +0000 Subject: [PATCH] From Marc Helbling, "please find enclosed a submission that should improve the VertexAccessOrderVisitor (pre-transform) optimizer: * it sorts primitives to keep "more complex" primitives first; maybe you'll prefer to have this as an option (but usually it should make more sense to pre-transform triangles before e.g. lines) * currently, the visitor rely on TriangleIndexFunctor and does not take care of points and lines (see https://github.com/openscenegraph/osg/blob/master/include/osg/TriangleIndexFunctor#L124-130). This can lead to issues e.g. if you store the wireframe lines along with some triangles: the triangles will be reindexed but not the line. I've therefore added osg/include/TriangleLinePointIndexFunctor to index triangles, lines and points and derived VertexReorder from this class. * to avoid issues, shared arrays are duplicated. However, in some cases (e.g. an UV channel shared in the geometry only) this is not required. I'm adding a SharedArrayOptimizer to optimize this: it looks for duplicated UVs before the array duplication and deduplicate arrays after. " git-svn-id: http://svn.openscenegraph.org/osg/OpenSceneGraph/trunk@14603 16af8721-9629-0410-8352-f15c8da7e697 --- include/osg/TriangleLinePointIndexFunctor | 257 ++++++++++++++++++++++ include/osgUtil/MeshOptimizers | 14 ++ src/osgUtil/MeshOptimizers.cpp | 92 +++++++- 3 files changed, 357 insertions(+), 6 deletions(-) create mode 100644 include/osg/TriangleLinePointIndexFunctor diff --git a/include/osg/TriangleLinePointIndexFunctor b/include/osg/TriangleLinePointIndexFunctor new file mode 100644 index 000000000..877e2a6ee --- /dev/null +++ b/include/osg/TriangleLinePointIndexFunctor @@ -0,0 +1,257 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab + * + * This application is open source and may be redistributed and/or modified + * freely and without restriction, both in commercial and non commercial + * applications, as long as this copyright notice is maintained. + * + * This application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef TRIANGLE_LINE_POINT_INDEX_FUNCTOR +#define TRIANGLE_LINE_POINT_INDEX_FUNCTOR + +#include +#include + + +template +class TriangleLinePointIndexFunctor : public osg::PrimitiveIndexFunctor, public T +{ +public: + virtual void setVertexArray(unsigned int,const osg::Vec2*) + {} + + virtual void setVertexArray(unsigned int ,const osg::Vec3* ) + {} + + virtual void setVertexArray(unsigned int,const osg::Vec4* ) + {} + + virtual void setVertexArray(unsigned int,const osg::Vec2d*) + {} + + virtual void setVertexArray(unsigned int ,const osg::Vec3d* ) + {} + + virtual void setVertexArray(unsigned int,const osg::Vec4d* ) + {} + + virtual void begin(GLenum mode) { + _modeCache = mode; + _indexCache.clear(); + } + + virtual void vertex(unsigned int vert) { + _indexCache.push_back(vert); + } + + virtual void end() { + if (!_indexCache.empty()) { + drawElements(_modeCache,_indexCache.size(),&_indexCache.front()); + } + } + + virtual void drawArrays(GLenum mode, GLint first, GLsizei count) { + switch(mode) + { + case(GL_TRIANGLES): + { + unsigned int pos=first; + for(GLsizei i = 2 ; i < count ; i += 3, pos += 3) { + this->operator()(pos, pos + 1, pos + 2); + } + break; + } + case(GL_TRIANGLE_STRIP): + { + unsigned int pos = first; + for(GLsizei i = 2 ; i < count ; ++ i, ++ pos) { + if ((i%2)) this->operator()(pos, pos + 2, pos + 1); + else this->operator()(pos, pos + 1, pos + 2); + } + break; + } + case(GL_QUADS): + { + unsigned int pos = first; + for(GLsizei i = 3 ; i < count ; i += 4, pos += 4) { + this->operator()(pos,pos + 1, pos + 2); + this->operator()(pos,pos + 2, pos + 3); + } + break; + } + case(GL_QUAD_STRIP): + { + unsigned int pos = first; + for(GLsizei i = 3 ; i < count ; i += 2, pos += 2) { + this->operator()(pos, pos + 1,pos + 2); + this->operator()(pos + 1,pos + 3,pos + 2); + } + break; + } + case(GL_POLYGON): // treat polygons as GL_TRIANGLE_FAN + case(GL_TRIANGLE_FAN): + { + unsigned int pos = first + 1; + for(GLsizei i = 2 ; i < count ; ++ i, ++ pos) { + this->operator()(first, pos, pos + 1); + } + break; + } + case(GL_LINES): + { + unsigned int pos = first; + for(GLsizei i = 0 ; i < count ; i += 2, pos += 2) { + this->operator()(pos, pos + 1); + } + break; + } + case(GL_LINE_STRIP): + { + unsigned int pos = first; + for(GLsizei i = 0 ; i < count - 1 ; i += 1, pos += 1) { + this->operator()(pos, pos + 1); + } + break; + } + case(GL_LINE_LOOP): + { + unsigned int pos = first; + for(GLsizei i = 0 ; i < count - 1 ; i += 1, pos += 1) { + this->operator()(pos, pos + 1); + } + this->operator()(pos, first); + break; + } + case(GL_POINTS): + { + unsigned int pos=first; + for(GLsizei i = 0 ; i < count ; ++ i) { + this->operator()(pos + i); + } + break; + } + default: + break; + } + } + + template + void drawElements(GLenum mode, GLsizei count, const I* indices) + { + typedef I Index; + typedef const I* IndexPointer; + + if (indices == 0 || count == 0) { + return; + } + + switch(mode) + { + case(GL_TRIANGLES): + { + IndexPointer ilast = &indices[count]; + for(IndexPointer iptr = indices ; iptr < ilast ; iptr += 3) { + this->operator()(*iptr, *(iptr + 1), *(iptr + 2)); + } + break; + } + case(GL_TRIANGLE_STRIP): + { + IndexPointer iptr = indices; + for(GLsizei i = 2 ; i < count ; ++ i, ++ iptr) { + if ((i%2)) this->operator()(*(iptr), *(iptr + 2), *(iptr + 1)); + else this->operator()(*(iptr), *(iptr + 1), *(iptr + 2)); + } + break; + } + case(GL_QUADS): + { + IndexPointer iptr = indices; + for(GLsizei i = 3 ; i < count ; i += 4, iptr += 4) { + this->operator()(*(iptr), *(iptr + 1), *(iptr + 2)); + this->operator()(*(iptr), *(iptr + 2), *(iptr + 3)); + } + break; + } + case(GL_QUAD_STRIP): + { + IndexPointer iptr = indices; + for(GLsizei i = 3 ; i < count ; i += 2, iptr += 2) { + this->operator()(*(iptr), *(iptr + 1), *(iptr + 2)); + this->operator()(*(iptr + 1), *(iptr + 3), *(iptr + 2)); + } + break; + } + case(GL_POLYGON): // treat polygons as GL_TRIANGLE_FAN + case(GL_TRIANGLE_FAN): + { + IndexPointer iptr = indices; + Index first = *iptr; + ++iptr; + for(GLsizei i = 2 ; i < count ; ++ i, ++ iptr) { + this->operator()(first, *(iptr), *(iptr + 1)); + } + break; + } + case(GL_LINES): + { + const I* iptr = indices; + for(GLsizei i = 0 ; i < count ; i += 2, iptr += 2) { + this->operator()(*iptr, *(iptr + 1)); + } + break; + } + case(GL_LINE_STRIP): + { + const I* iptr = indices; + for(GLsizei i = 0 ; i < count - 1 ; i += 1, iptr += 1) { + this->operator()(*iptr, *(iptr + 1)); + } + break; + } + case(GL_LINE_LOOP): + { + const I* iptr = indices; + I first = *iptr; + for(GLsizei i = 0 ; i < count - 1 ; i += 1, iptr += 1) { + this->operator()(*iptr, *(iptr + 1)); + } + this->operator()(*iptr, first); + break; + } + case GL_POINTS: + { + IndexPointer ilast = &indices[count]; + for(IndexPointer iptr = indices ; iptr < ilast ; iptr += 1) { + this->operator()(*iptr); + } + break; + } + default: + break; + } + } + + virtual void drawElements(GLenum mode, GLsizei count, const GLubyte* indices) { + drawElements(mode, count, indices); + } + + virtual void drawElements(GLenum mode,GLsizei count,const GLushort* indices) { + drawElements(mode, count, indices); + } + + virtual void drawElements(GLenum mode,GLsizei count,const GLuint* indices) { + drawElements(mode, count, indices); + } + + + GLenum _modeCache; + std::vector _indexCache; + std::vector _remap; +}; + +#endif diff --git a/include/osgUtil/MeshOptimizers b/include/osgUtil/MeshOptimizers index 9ff9a63b4..c35bcfc6c 100644 --- a/include/osgUtil/MeshOptimizers +++ b/include/osgUtil/MeshOptimizers @@ -89,6 +89,20 @@ protected: // attributes in the order they are used. class OSGUTIL_EXPORT VertexAccessOrderVisitor : public GeometryCollector { + struct OrderByPrimitiveMode + { + inline bool operator() (const osg::ref_ptr& prim1, const osg::ref_ptr& prim2) + { + if(prim1 && prim2) { + return prim1->getMode() >= prim2->getMode(); + } + else if(prim1) { + return true; + } + return false; + } + } order_by_primitive_mode; + public: VertexAccessOrderVisitor(Optimizer* optimizer = 0) : GeometryCollector(optimizer, Optimizer::VERTEX_PRETRANSFORM) diff --git a/src/osgUtil/MeshOptimizers.cpp b/src/osgUtil/MeshOptimizers.cpp index e157df720..4fe84feb6 100644 --- a/src/osgUtil/MeshOptimizers.cpp +++ b/src/osgUtil/MeshOptimizers.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -98,6 +99,60 @@ struct GeometryArrayGatherer ArrayList _arrayList; }; +class SharedArrayOptimizer { +public: + void findDuplicatedUVs(const osg::Geometry& geometry) + { + _deduplicateUvs.clear(); + + // look for all channels that are shared only *within* the geometry + std::map arrayPointerCounter; + + for(unsigned int id = 0 ; id < geometry.getNumTexCoordArrays() ; ++ id) { + const osg::Array* channel = geometry.getTexCoordArray(id); + if(channel && channel->getNumElements()) { + if(arrayPointerCounter.find(channel) == arrayPointerCounter.end()) { + arrayPointerCounter[channel] = 1; + } + else { + arrayPointerCounter[channel] += 1; + } + } + } + + std::map references; + + for(unsigned int id = 0 ; id != geometry.getNumTexCoordArrays() ; ++ id) { + const osg::Array* channel = geometry.getTexCoordArray(id); + // test if array is shared outside the geometry + if(channel && static_cast(channel->referenceCount()) == arrayPointerCounter[channel]) { + std::map::const_iterator reference = references.find(channel); + if(reference == references.end()) { + references[channel] = id; + } + else { + _deduplicateUvs[id] = reference->second; + } + } + } + } + + void deduplicateUVs(osg::Geometry& geometry) + { + for(std::map::const_iterator it_duplicate = _deduplicateUvs.begin() ; + it_duplicate != _deduplicateUvs.end() ; ++ it_duplicate) { + osg::Array* original = geometry.getTexCoordArray(it_duplicate->second); + geometry.setTexCoordArray(it_duplicate->first, + original, + (original ? original->getBinding() : osg::Array::BIND_UNDEFINED)); + } + } + +protected: + std::map _deduplicateUvs; +}; + + // Compare vertices in a mesh using all their attributes. The vertices // are identified by their index. Extracted from TriStripVisitor.cpp struct VertexAttribComparitor : public GeometryArrayGatherer @@ -1059,11 +1114,11 @@ public: const unsigned Remapper::invalidIndex = std::numeric_limits::max(); -// Record the order in which vertices in a Geometry are used. +// Record the order in which vertices in a Geometry are used in triangle, line or point primitives. struct VertexReorderOperator { unsigned seq; - vector remap; + std::vector remap; VertexReorderOperator() : seq(0) { @@ -1071,24 +1126,36 @@ struct VertexReorderOperator void inline doVertex(unsigned v) { - if (remap[v] == Remapper::invalidIndex) - remap[v] = seq++; + if (remap[v] == Remapper::invalidIndex) { + remap[v] = seq ++; + } } + void operator()(unsigned p1, unsigned p2, unsigned p3) { doVertex(p1); doVertex(p2); doVertex(p3); } + + void operator()(unsigned p1, unsigned p2) + { + doVertex(p1); + doVertex(p2); + } + + void operator()(unsigned p1) + { + doVertex(p1); + } }; -struct VertexReorder : public TriangleIndexFunctor +struct VertexReorder : public TriangleLinePointIndexFunctor { VertexReorder(unsigned numVerts) { remap.resize(numVerts, Remapper::invalidIndex); } - }; } @@ -1119,7 +1186,12 @@ void VertexAccessOrderVisitor::optimizeOrder(Geometry& geom) Array* vertArray = geom.getVertexArray(); if (!vertArray || vertArray->getNumElements()==0) return; + Geometry::PrimitiveSetList& primSets = geom.getPrimitiveSetList(); + + // sort primitives: first triangles, then lines and finally points + std::sort(primSets.begin(), primSets.end(), order_by_primitive_mode); + VertexReorder vr(vertArray->getNumElements()); for (Geometry::PrimitiveSetList::iterator itr = primSets.begin(), end = primSets.end(); @@ -1135,6 +1207,10 @@ void VertexAccessOrderVisitor::optimizeOrder(Geometry& geom) ps->accept(vr); } + // search for UVs array shared only within the geometry + SharedArrayOptimizer deduplicator; + deduplicator.findDuplicatedUVs(geom); + // duplicate shared arrays as it isn't safe to rearrange vertices when arrays are shared. if (geom.containsSharedArrays()) geom.duplicateSharedArrays(); GeometryArrayGatherer gatherer(geom); @@ -1162,6 +1238,10 @@ void VertexAccessOrderVisitor::optimizeOrder(Geometry& geom) break; } } + + // deduplicate UVs array that were only shared within the geometry + deduplicator.deduplicateUVs(geom); + geom.dirtyDisplayList(); } }