/* -*-c++-*- OpenSceneGraph - Copyright (C) Sketchfab */ #ifndef GLES_UTIL #define GLES_UTIL #include #include #include #include #include #include #include #include #include #include #include #include "StatLogger" namespace glesUtil { using namespace std; using namespace osg; typedef std::vector IndexList; // A helper class that gathers up all the attribute arrays of an // osg::Geometry object that are BIND_PER_VERTEX and runs an // ArrayVisitor on them. struct GeometryArrayGatherer { typedef std::vector ArrayList; GeometryArrayGatherer(osg::Geometry& geometry) { add(geometry.getVertexArray()); add(geometry.getNormalArray()); add(geometry.getColorArray()); add(geometry.getSecondaryColorArray()); add(geometry.getFogCoordArray()); for(unsigned int i = 0 ; i < geometry.getNumTexCoordArrays() ; ++ i) { add(geometry.getTexCoordArray(i)); } for(unsigned int i = 0 ; i < geometry.getNumVertexAttribArrays() ; ++ i) { add(geometry.getVertexAttribArray(i)); } } void add(osg::Array* array) { if (array) { _arrayList.push_back(array); } } void accept(osg::ArrayVisitor& av) { for(ArrayList::iterator itr = _arrayList.begin() ; itr != _arrayList.end(); ++ itr) { (*itr)->accept(av); } } ArrayList _arrayList; }; // Compact the vertex attribute arrays. Also stolen from TriStripVisitor class RemapArray : public osg::ArrayVisitor { public: RemapArray(const IndexList& remapping) : _remapping(remapping) {} const IndexList& _remapping; template inline void remap(T& array) { for(unsigned int i = 0 ; i < _remapping.size() ; ++ i) { if(i != _remapping[i]) { array[i] = array[_remapping[i]]; } } array.erase(array.begin() + _remapping.size(), array.end()); } virtual void apply(osg::Array&) {} virtual void apply(osg::ByteArray& array) { remap(array); } virtual void apply(osg::ShortArray& array) { remap(array); } virtual void apply(osg::IntArray& array) { remap(array); } virtual void apply(osg::UByteArray& array) { remap(array); } virtual void apply(osg::UShortArray& array) { remap(array); } virtual void apply(osg::UIntArray& array) { remap(array); } virtual void apply(osg::FloatArray& array) { remap(array); } virtual void apply(osg::DoubleArray& array) { remap(array); } virtual void apply(osg::Vec2dArray& array) { remap(array); } virtual void apply(osg::Vec3dArray& array) { remap(array); } virtual void apply(osg::Vec4dArray& array) { remap(array); } virtual void apply(osg::Vec2Array& array) { remap(array); } virtual void apply(osg::Vec3Array& array) { remap(array); } virtual void apply(osg::Vec4Array& array) { remap(array); } virtual void apply(osg::Vec2iArray& array) { remap(array); } virtual void apply(osg::Vec3iArray& array) { remap(array); } virtual void apply(osg::Vec4iArray& array) { remap(array); } virtual void apply(osg::Vec2uiArray& array) { remap(array); } virtual void apply(osg::Vec3uiArray& array) { remap(array); } virtual void apply(osg::Vec4uiArray& array) { remap(array); } virtual void apply(osg::Vec2sArray& array) { remap(array); } virtual void apply(osg::Vec3sArray& array) { remap(array); } virtual void apply(osg::Vec4sArray& array) { remap(array); } virtual void apply(osg::Vec2usArray& array) { remap(array); } virtual void apply(osg::Vec3usArray& array) { remap(array); } virtual void apply(osg::Vec4usArray& array) { remap(array); } virtual void apply(osg::Vec2bArray& array) { remap(array); } virtual void apply(osg::Vec3bArray& array) { remap(array); } virtual void apply(osg::Vec4bArray& array) { remap(array); } virtual void apply(osg::Vec4ubArray& array) { remap(array); } virtual void apply(osg::Vec3ubArray& array) { remap(array); } virtual void apply(osg::Vec2ubArray& array) { remap(array); } virtual void apply(osg::MatrixfArray& array) { remap(array); } protected: RemapArray& operator= (const RemapArray&) { return *this; } }; // Compare vertices in a mesh using all their attributes. The vertices // are identified by their index. Extracted from TriStripVisitor.cpp struct VertexAttribComparitor : public GeometryArrayGatherer { VertexAttribComparitor(osg::Geometry& geometry) : GeometryArrayGatherer(geometry) {} bool operator() (unsigned int lhs, unsigned int rhs) const { for(ArrayList::const_iterator itr = _arrayList.begin(); itr != _arrayList.end(); ++ itr) { int compare = (*itr)->compare(lhs, rhs); if (compare == -1) { return true; } if (compare == 1) { return false; } } return false; } int compare(unsigned int lhs, unsigned int rhs) { for(ArrayList::iterator itr = _arrayList.begin(); itr != _arrayList.end(); ++ itr) { int compare = (*itr)->compare(lhs, rhs); if (compare == -1) { return -1; } if (compare == 1) { return 1; } } return 0; } protected: VertexAttribComparitor& operator= (const VertexAttribComparitor&) { return *this; } }; // Move the values in an array to new positions, based on the // remapping table. remapping[i] contains element i's new position, if // any. Unlike RemapArray in TriStripVisitor, this code doesn't // assume that elements only move downward in the array. class Remapper : public osg::ArrayVisitor { public: static const unsigned invalidIndex; Remapper(const vector& remapping) : _remapping(remapping), _newsize(0) { for (vector::const_iterator itr = _remapping.begin(), end = _remapping.end(); itr != end; ++itr) if (*itr != invalidIndex) ++_newsize; } const vector& _remapping; size_t _newsize; template inline void remap(T& array) { ref_ptr newarray = new T(_newsize); T* newptr = newarray.get(); for (size_t i = 0; i < _remapping.size(); ++i) { if (_remapping[i] != invalidIndex) { (*newptr)[_remapping[i]] = array[i]; } } array.swap(*newptr); } virtual void apply(osg::Array&) {} virtual void apply(osg::ByteArray& array) { remap(array); } virtual void apply(osg::ShortArray& array) { remap(array); } virtual void apply(osg::IntArray& array) { remap(array); } virtual void apply(osg::UByteArray& array) { remap(array); } virtual void apply(osg::UShortArray& array) { remap(array); } virtual void apply(osg::UIntArray& array) { remap(array); } virtual void apply(osg::FloatArray& array) { remap(array); } virtual void apply(osg::DoubleArray& array) { remap(array); } virtual void apply(osg::Vec2Array& array) { remap(array); } virtual void apply(osg::Vec3Array& array) { remap(array); } virtual void apply(osg::Vec4Array& array) { remap(array); } virtual void apply(osg::Vec2bArray& array) { remap(array); } virtual void apply(osg::Vec3bArray& array) { remap(array); } virtual void apply(osg::Vec4bArray& array) { remap(array); } virtual void apply(osg::Vec2sArray& array) { remap(array); } virtual void apply(osg::Vec3sArray& array) { remap(array); } virtual void apply(osg::Vec4sArray& array) { remap(array); } virtual void apply(osg::Vec2iArray& array) { remap(array); } virtual void apply(osg::Vec3iArray& array) { remap(array); } virtual void apply(osg::Vec4iArray& array) { remap(array); } virtual void apply(osg::Vec2dArray& array) { remap(array); } virtual void apply(osg::Vec3dArray& array) { remap(array); } virtual void apply(osg::Vec4dArray& array) { remap(array); } virtual void apply(osg::Vec2ubArray& array) { remap(array); } virtual void apply(osg::Vec3ubArray& array) { remap(array); } virtual void apply(osg::Vec4ubArray& array) { remap(array); } virtual void apply(osg::Vec2usArray& array) { remap(array); } virtual void apply(osg::Vec3usArray& array) { remap(array); } virtual void apply(osg::Vec4usArray& array) { remap(array); } virtual void apply(osg::Vec2uiArray& array) { remap(array); } virtual void apply(osg::Vec3uiArray& array) { remap(array); } virtual void apply(osg::Vec4uiArray& array) { remap(array); } virtual void apply(osg::MatrixfArray& array) { remap(array); } virtual void apply(osg::MatrixdArray& array) { remap(array); } }; // Record the order in which vertices in a Geometry are used. struct VertexReorderOperator { unsigned seq; std::vector remap; VertexReorderOperator() : seq(0) { } void inline doVertex(unsigned v) { if (remap[v] == glesUtil::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 TriangleLinePointIndexFunctor { VertexReorder(unsigned numVerts) { remap.resize(numVerts, glesUtil::Remapper::invalidIndex); } }; inline osg::DrawElementsUInt* reorderDrawElements(PrimitiveSet* primitive, const vector& reorder) { osg::DrawElementsUInt* newElements = new osg::DrawElementsUInt(primitive->getMode()); for (unsigned int i = 0 ; i < primitive->getNumIndices() ; ++ i) { newElements->addElement(static_cast(reorder[primitive->index(i)])); } newElements->setUserDataContainer(primitive->getUserDataContainer()); return newElements; } class VertexAccessOrderVisitor : osgUtil::VertexAccessOrderVisitor { struct OrderByPrimitiveMode { inline bool operator() (const osg::ref_ptr prim1, const osg::ref_ptr prim2) { if(prim1.get() && prim2.get()) { return prim1->getMode() > prim2->getMode(); } else if(prim1.get()) { return true; } return false; } } order_by_primitive_mode; public: void remapTargetVertices(Remapper remapper, Geometry& geom) { if(osgAnimation::MorphGeometry *morphGeometry = dynamic_cast(&geom)) { osgAnimation::MorphGeometry::MorphTargetList targetList = morphGeometry->getMorphTargetList(); for (osgAnimation::MorphGeometry::MorphTargetList::iterator ti = targetList.begin(); ti != targetList.end(); ++ti) { osgAnimation::MorphGeometry::MorphTarget *morphTarget = &(*ti); osg::Geometry *target = morphTarget->getGeometry(); GeometryArrayGatherer gatherer(*target); gatherer.accept(remapper); } } } void optimizeOrder(Geometry& geom) { StatLogger logger("glesUtil::VertexAccessOrderVisitor::optimizeOrder(" + geom.getName() + ")"); Array* vertArray = geom.getVertexArray(); if (!vertArray || !vertArray->getNumElements()) 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); glesUtil::VertexReorder vr(vertArray->getNumElements()); for (Geometry::PrimitiveSetList::iterator itr = primSets.begin(), end = primSets.end(); itr != end; ++itr) { PrimitiveSet* ps = itr->get(); PrimitiveSet::Type type = ps->getType(); if (type != PrimitiveSet::DrawElementsUBytePrimitiveType && type != PrimitiveSet::DrawElementsUShortPrimitiveType && type != PrimitiveSet::DrawElementsUIntPrimitiveType) return; ps->accept(vr); } // search for UVs array shared only within the geometry osgUtil::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); Remapper remapper(vr.remap); gatherer.accept(remapper); //Remap morphGeometry target remapTargetVertices(remapper, geom); Geometry::PrimitiveSetList newPrimitives; for (Geometry::PrimitiveSetList::iterator itr = primSets.begin(), end = primSets.end(); itr != end; ++itr) { PrimitiveSet* ps = itr->get(); newPrimitives.push_back(reorderDrawElements(ps, vr.remap)); } geom.setPrimitiveSetList(newPrimitives); // deduplicate UVs array that were only shared within the geometry deduplicator.deduplicateUVs(geom); geom.dirtyDisplayList(); } }; } // glesUtil namespace #endif