/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or * (at your option) any later version. The full license is in LICENSE file * included with this distribution, and on the openscenegraph.org website. * * This library 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. See the * OpenSceneGraph Public License for more details. */ #include #include #include #include #include #include #include #include using namespace osgUtil; struct dereference_less { template inline bool operator() (const T& lhs,const U& rhs) const { return *lhs < *rhs; } }; template bool dereference_check_less(const T& lhs,const T& rhs) { if (lhs==rhs) return false; if (!lhs) return true; if (!rhs) return false; return *lhs < *rhs; } struct dereference_clear { template inline void operator() (const T& t) { T& non_const_t = const_cast(t); non_const_t->clear(); } }; class EdgeCollapse { public: #if 1 typedef float error_type; #else typedef double error_type; #endif struct Triangle; struct Edge; struct Point; EdgeCollapse(): _geometry(0), _computeErrorMetricUsingLength(false) {} ~EdgeCollapse(); void setGeometry(osg::Geometry* geometry, const Simplifier::IndexList& protectedPoints); osg::Geometry* getGeometry() { return _geometry; } void setComputeErrorMetricUsingLength(bool flag) { _computeErrorMetricUsingLength = flag; } bool getComputeErrorMetricUsingLength() const { return _computeErrorMetricUsingLength; } unsigned int getNumOfTriangles() { return _triangleSet.size(); } Point* computeInterpolatedPoint(Edge* edge,float r) const { Point* p1 = edge->_p1.get(); Point* p2 = edge->_p2.get(); if (p1==0 || p2==0) { OSG_NOTICE<<"Error computeInterpolatedPoint("<_vertex = p1->_vertex * r1 + p2->_vertex * r2; unsigned int s = osg::minimum(p1->_attributes.size(),p2->_attributes.size()); for(unsigned int i=0;i_attributes.push_back(p1->_attributes[i]*r1 + p2->_attributes[i]*r2); } return point; } Point* computeOptimalPoint(Edge* edge) const { return computeInterpolatedPoint(edge,0.5f); } error_type computeErrorMetric(Edge* edge,Point* point) const { if (_computeErrorMetricUsingLength) { error_type dx = error_type(edge->_p1->_vertex.x()) - error_type(edge->_p2->_vertex.x()); error_type dy = error_type(edge->_p1->_vertex.y()) - error_type(edge->_p2->_vertex.y()); error_type dz = error_type(edge->_p1->_vertex.z()) - error_type(edge->_p2->_vertex.z()); return sqrt(dx*dx + dy*dy + dz*dz); } else if (point) { typedef std::set< osg::ref_ptr > LocalTriangleSet ; LocalTriangleSet triangles; std::copy( edge->_p1->_triangles.begin(), edge->_p1->_triangles.end(), std::inserter(triangles,triangles.begin())); std::copy( edge->_p2->_triangles.begin(), edge->_p2->_triangles.end(), std::inserter(triangles,triangles.begin())); const osg::Vec3& vertex = point->_vertex; error_type error = 0.0; if (triangles.empty()) return 0.0; for(LocalTriangleSet::iterator itr=triangles.begin(); itr!=triangles.end(); ++itr) { error += fabs( (*itr)->distance(vertex) ); } // use average of error error /= error_type(triangles.size()); return error; } else { return 0; } } void updateErrorMetricForEdge(Edge* edge) { if (!edge->_p1 || !edge->_p2) { OSG_NOTICE<<"Error updateErrorMetricForEdge("< keep_local_reference_to_edge(edge); if (_edgeSet.count(keep_local_reference_to_edge)!=0) { _edgeSet.erase(keep_local_reference_to_edge); } edge->_proposedPoint = computeOptimalPoint(edge); if (_computeErrorMetricUsingLength) { edge->setErrorMetric( computeErrorMetric( edge, edge->_proposedPoint.get())); } else { edge->updateMaxNormalDeviationOnEdgeCollapse(); if (edge->getMaxNormalDeviationOnEdgeCollapse()<=1.0 && !edge->isAdjacentToBoundary()) edge->setErrorMetric( computeErrorMetric( edge, edge->_proposedPoint.get())); else edge->setErrorMetric( FLT_MAX ); } _edgeSet.insert(keep_local_reference_to_edge); } void updateErrorMetricForAllEdges() { typedef std::vector< osg::ref_ptr > LocalEdgeList ; LocalEdgeList edges; std::copy( _edgeSet.begin(), _edgeSet.end(), std::back_inserter(edges)); _edgeSet.clear(); for(LocalEdgeList::iterator itr=edges.begin(); itr!=edges.end(); ++itr) { Edge* edge = itr->get(); if (_computeErrorMetricUsingLength) { edge->setErrorMetric( computeErrorMetric( edge, edge->_proposedPoint.get())); } else { edge->_proposedPoint = computeOptimalPoint(edge); edge->updateMaxNormalDeviationOnEdgeCollapse(); if (edge->getMaxNormalDeviationOnEdgeCollapse()<=1.0 && !edge->isAdjacentToBoundary()) edge->setErrorMetric( computeErrorMetric( edge, edge->_proposedPoint.get())); else edge->setErrorMetric( FLT_MAX ); } _edgeSet.insert(edge); } } bool collapseMinimumErrorEdge() { if (!_edgeSet.empty()) { Edge* edge = const_cast(_edgeSet.begin()->get()); if (edge->getErrorMetric()==FLT_MAX) { OSG_INFO<<"collapseMinimumErrorEdge() return false due to edge->getErrorMetric()==FLT_MAX"< pNew = edge->_proposedPoint.valid()? edge->_proposedPoint.get() : computeInterpolatedPoint(edge,0.5f); return (collapseEdge(edge,pNew.get())); } OSG_INFO<<"collapseMinimumErrorEdge() return false due to _edgeSet.empty()"<(_edgeSet.rbegin()->get()); if (edge->getErrorMetric()==FLT_MAX) { OSG_INFO<<"divideLongestEdge() return false due to edge->getErrorMetric()==FLT_MAX"< pNew = edge->_proposedPoint.valid()? edge->_proposedPoint.get() : computeInterpolatedPoint(edge,0.5f); return (divideEdge(edge,pNew.get())); } OSG_INFO<<"divideLongestEdge() return false due to _edgeSet.empty()"< FloatList; typedef std::set,dereference_less > EdgeSet; typedef std::set< osg::ref_ptr,dereference_less > PointSet; typedef std::vector< osg::ref_ptr > PointList; typedef std::list< osg::ref_ptr > TriangleList; typedef std::set< osg::ref_ptr > TriangleSet; typedef std::map< osg::ref_ptr, unsigned int, dereference_less > TriangleMap; struct Point : public osg::Referenced { Point(): _protected(false), _index(0) {} bool _protected; unsigned int _index; osg::Vec3 _vertex; FloatList _attributes; TriangleSet _triangles; void clear() { _attributes.clear(); _triangles.clear(); } bool operator < ( const Point& rhs) const { if (_vertex < rhs._vertex) return true; if (rhs._vertex < _vertex) return false; return _attributes < rhs._attributes; } bool isBoundaryPoint() const { if (_protected) return true; for(TriangleSet::const_iterator itr=_triangles.begin(); itr!=_triangles.end(); ++itr) { const Triangle* triangle = itr->get(); if ((triangle->_e1->_p1==this || triangle->_e1->_p2==this) && triangle->_e1->isBoundaryEdge()) return true; if ((triangle->_e2->_p1==this || triangle->_e2->_p2==this) && triangle->_e2->isBoundaryEdge()) return true; if ((triangle->_e3->_p1==this || triangle->_e3->_p2==this) && triangle->_e3->isBoundaryEdge()) return true; //if ((*itr)->isBoundaryTriangle()) return true; } return false; } }; struct Edge : public osg::Referenced { Edge(): _errorMetric(0.0), _maximumDeviation(1.0) {} void clear() { _p1 = 0; _p2 = 0; _triangles.clear(); } osg::ref_ptr _p1; osg::ref_ptr _p2; TriangleSet _triangles; error_type _errorMetric; error_type _maximumDeviation; osg::ref_ptr _proposedPoint; void setErrorMetric(error_type errorMetric) { _errorMetric = errorMetric; } error_type getErrorMetric() const { return _errorMetric; } bool operator < ( const Edge& rhs) const { // both error metrics are computed if (getErrorMetric()2) OSG_NOTICE<<"Warning too many triangles ("<<_triangles.size()<<") sharing edge "<isBoundaryPoint() || _p2->isBoundaryPoint(); } void updateMaxNormalDeviationOnEdgeCollapse() { //OSG_NOTICE<<"updateMaxNormalDeviationOnEdgeCollapse()"<_triangles.begin(); itr1!=_p1->_triangles.end(); ++itr1) { if (_triangles.count(*itr1)==0) { _maximumDeviation = osg::maximum(_maximumDeviation, (*itr1)->computeNormalDeviationOnEdgeCollapse(this,_proposedPoint.get())); } } for(TriangleSet::iterator itr2=_p2->_triangles.begin(); itr2!=_p2->_triangles.end(); ++itr2) { if (_triangles.count(*itr2)==0) { _maximumDeviation = osg::maximum(_maximumDeviation, (*itr2)->computeNormalDeviationOnEdgeCollapse(this,_proposedPoint.get())); } } } error_type getMaxNormalDeviationOnEdgeCollapse() const { return _maximumDeviation; } }; struct Triangle : public osg::Referenced { Triangle() {} void clear() { _p1 = 0; _p2 = 0; _p3 = 0; _e1 = 0; _e2 = 0; _e3 = 0; } inline bool operator < (const Triangle& rhs) const { if (dereference_check_less(_p1,rhs._p1)) return true; if (dereference_check_less(rhs._p1,_p1)) return false; const Point* lhs_lower = dereference_check_less(_p2,_p3) ? _p2.get() : _p3.get(); const Point* rhs_lower = dereference_check_less(rhs._p2,rhs._p3) ? rhs._p2.get() : rhs._p3.get(); if (dereference_check_less(lhs_lower,rhs_lower)) return true; if (dereference_check_less(rhs_lower,lhs_lower)) return false; const Point* lhs_upper = dereference_check_less(_p2,_p3) ? _p3.get() : _p2.get(); const Point* rhs_upper = dereference_check_less(rhs._p2,rhs._p3) ? rhs._p3.get() : rhs._p2.get(); return dereference_check_less(lhs_upper,rhs_upper); } void setOrderedPoints(Point* p1, Point* p2, Point* p3) { Point* points[3]; points[0] = p1; points[1] = p2; points[2] = p3; // find the lowest value point in the list. unsigned int lowest = 0; if (dereference_check_less(points[1],points[lowest])) lowest = 1; if (dereference_check_less(points[2],points[lowest])) lowest = 2; _p1 = points[lowest]; _p2 = points[(lowest+1)%3]; _p3 = points[(lowest+2)%3]; } void update() { _plane.set(_p1->_vertex,_p2->_vertex,_p3->_vertex); } osg::Plane computeNewPlaneOnEdgeCollapse(Edge* edge,Point* pNew) const { const Point* p1 = (_p1==edge->_p1 || _p1==edge->_p2) ? pNew : _p1.get(); const Point* p2 = (_p2==edge->_p1 || _p2==edge->_p2) ? pNew : _p2.get(); const Point* p3 = (_p3==edge->_p1 || _p3==edge->_p2) ? pNew : _p3.get(); return osg::Plane(p1->_vertex,p2->_vertex,p3->_vertex); } // note return 1 - dotproduct, so that deviation is in the range of 0.0 to 2.0, where 0 is coincident, 1.0 is 90 degrees, and 2.0 is 180 degrees. error_type computeNormalDeviationOnEdgeCollapse(Edge* edge,Point* pNew) const { const Point* p1 = (_p1==edge->_p1 || _p1==edge->_p2) ? pNew : _p1.get(); const Point* p2 = (_p2==edge->_p1 || _p2==edge->_p2) ? pNew : _p2.get(); const Point* p3 = (_p3==edge->_p1 || _p3==edge->_p2) ? pNew : _p3.get(); osg::Vec3 new_normal = (p2->_vertex - p1->_vertex) ^ (p3->_vertex - p2->_vertex); new_normal.normalize(); error_type result = 1.0 - (new_normal.x() * _plane[0] + new_normal.y() * _plane[1] + new_normal.z() * _plane[2]); return result; } error_type distance(const osg::Vec3& vertex) const { return error_type(_plane[0])*error_type(vertex.x())+ error_type(_plane[1])*error_type(vertex.y())+ error_type(_plane[2])*error_type(vertex.z())+ error_type(_plane[3]); } bool isBoundaryTriangle() const { return (_e1->isBoundaryEdge() || _e2->isBoundaryEdge() || _e3->isBoundaryEdge()); } osg::ref_ptr _p1; osg::ref_ptr _p2; osg::ref_ptr _p3; osg::ref_ptr _e1; osg::ref_ptr _e2; osg::ref_ptr _e3; osg::Plane _plane; }; Triangle* addTriangle(unsigned int p1, unsigned int p2, unsigned int p3) { //OSG_NOTICE<<"addTriangle("<_p1 = points[lowest]; triangle->_p2 = points[(lowest+1)%3]; triangle->_p3 = points[(lowest+2)%3]; triangle->_e1 = addEdge(triangle, triangle->_p1.get(), triangle->_p2.get()); triangle->_e2 = addEdge(triangle, triangle->_p2.get(), triangle->_p3.get()); triangle->_e3 = addEdge(triangle, triangle->_p3.get(), triangle->_p1.get()); triangle->update(); _triangleSet.insert(triangle); return triangle; } Triangle* addTriangle(Point* p1, Point* p2, Point* p3) { // OSG_NOTICE<<" addTriangle("<_p1 = points[lowest]; triangle->_p2 = points[(lowest+1)%3]; triangle->_p3 = points[(lowest+2)%3]; triangle->_e1 = addEdge(triangle, triangle->_p1.get(), triangle->_p2.get()); triangle->_e2 = addEdge(triangle, triangle->_p2.get(), triangle->_p3.get()); triangle->_e3 = addEdge(triangle, triangle->_p3.get(), triangle->_p1.get()); triangle->update(); _triangleSet.insert(triangle); return triangle; } void removeTriangle(Triangle* triangle) { if (triangle->_p1.valid()) removePoint(triangle,triangle->_p1.get()); if (triangle->_p2.valid()) removePoint(triangle,triangle->_p2.get()); if (triangle->_p3.valid()) removePoint(triangle,triangle->_p3.get()); if (triangle->_e1.valid()) removeEdge(triangle,triangle->_e1.get()); if (triangle->_e2.valid()) removeEdge(triangle,triangle->_e2.get()); if (triangle->_e3.valid()) removeEdge(triangle,triangle->_e3.get()); _triangleSet.erase(triangle); } void replaceTrianglePoint(Triangle* triangle, Point* pOriginal, Point* pNew) { if (triangle->_p1==pOriginal || triangle->_p2==pOriginal || triangle->_p3==pOriginal) { // fix the corner points to use the new point if (triangle->_p1==pOriginal) triangle->_p1=pNew; if (triangle->_p2==pOriginal) triangle->_p2=pNew; if (triangle->_p3==pOriginal) triangle->_p3=pNew; // fixes the edges so they point to use the new point triangle->_e1 = replaceEdgePoint(triangle->_e1.get(),pOriginal,pNew); triangle->_e2 = replaceEdgePoint(triangle->_e2.get(),pOriginal,pNew); triangle->_e3 = replaceEdgePoint(triangle->_e3.get(),pOriginal,pNew); // remove the triangle form the original point, and possibly the point if its the last triangle to use it removePoint(triangle, pOriginal); // add the triangle to that point addPoint(triangle,pNew); } } unsigned int testTriangle(Triangle* triangle) { unsigned int result = 0; if (!(triangle->_p1)) { OSG_NOTICE<<"testTriangle("<_p1->_triangles.count(triangle)==0) { OSG_NOTICE<<"testTriangle("<_triangles does not contain triangle"<_p2)) { OSG_NOTICE<<"testTriangle("<_p2->_triangles.count(triangle)==0) { OSG_NOTICE<<"testTriangle("<_triangles does not contain triangle"<_p3)) { OSG_NOTICE<<"testTriangle("<_p3->_triangles.count(triangle)==0) { OSG_NOTICE<<"testTriangle("<_triangles does not contain triangle"<_e1.get())) { ++result; OSG_NOTICE<<"testTriangle("<_e2.get())) { ++result; OSG_NOTICE<<"testTriangle("<_e3.get())) { OSG_NOTICE<<"testTriangle("<(itr->get())); } return numErrors; } Edge* addEdge(Triangle* triangle, Point* p1, Point* p2) { // OSG_NOTICE<<" addEdge("< edge = new Edge; if (dereference_check_less(p1, p2)) { edge->_p1 = p1; edge->_p2 = p2; } else { edge->_p1 = p2; edge->_p2 = p1; } edge->setErrorMetric( computeErrorMetric( edge.get(), edge->_proposedPoint.get())); EdgeSet::iterator itr = _edgeSet.find(edge); if (itr==_edgeSet.end()) { // OSG_NOTICE<<" addEdge("<_p1="<_p1.get()<<" _p2="<_p2.get()<_p1="<_p1.get()<<" _p2="<_p2.get()<addTriangle(triangle); return edge.get(); } void removeEdge(Triangle* triangle, Edge* edge) { EdgeSet::iterator itr = _edgeSet.find(edge); if (itr!=_edgeSet.end()) { edge->_triangles.erase(triangle); if (edge->_triangles.empty()) { edge->_p1 = 0; edge->_p2 = 0; // edge no longer in use, so need to delete. _edgeSet.erase(itr); } } } Edge* replaceEdgePoint(Edge* edge, Point* pOriginal, Point* pNew) { if (edge->_p1==pOriginal || edge->_p2==pOriginal) { EdgeSet::iterator itr = _edgeSet.find(edge); if (itr!=_edgeSet.end()) { // remove the edge from the list, as its positoin in the list // may need to change once its values have been amended _edgeSet.erase(itr); } // modify its values if (edge->_p1==pOriginal) edge->_p1=pNew; if (edge->_p2==pOriginal) edge->_p2=pNew; if (dereference_check_less(edge->_p2,edge->_p1)) { edge->_p1.swap(edge->_p2); } itr = _edgeSet.find(edge); if (itr!=_edgeSet.end()) { // reuse existing edge. edge = const_cast(itr->get()); } else { // put it back in. _edgeSet.insert(edge); } return edge; } else { return edge; } } bool collapseEdge(Edge* edge, Point* pNew) { //if (edge->_triangles.size()<2) return false; //if (edge->_triangles.size()>2) return false; #ifdef ORIGIANAL_CODE if (edge->getMaxNormalDeviationOnEdgeCollapse()>1.0) { OSG_NOTICE<<"collapseEdge("<getMaxNormalDeviationOnEdgeCollapse() = "<getMaxNormalDeviationOnEdgeCollapse()<getMaxNormalDeviationOnEdgeCollapse() = "<getMaxNormalDeviationOnEdgeCollapse()< > LocalEdgeList; osg::ref_ptr keep_edge_locally_referenced_to_prevent_premature_deletion = edge; osg::ref_ptr keep_point_locally_referenced_to_prevent_premature_deletion = pNew; osg::ref_ptr edge_p1 = edge->_p1; osg::ref_ptr edge_p2 = edge->_p2; TriangleMap triangleMap; TriangleList triangles_p1; TriangleList triangles_p2; LocalEdgeList oldEdges; if (edge_p1 != pNew) { for(TriangleSet::iterator itr=edge_p1->_triangles.begin(); itr!=edge_p1->_triangles.end(); ++itr) { if (edge->_triangles.count(*itr)==0) { Triangle* triangle = const_cast(itr->get()); triangles_p1.push_back(triangle); oldEdges.insert(triangle->_e1); oldEdges.insert(triangle->_e2); oldEdges.insert(triangle->_e3); } } //triangles_p1 = edge_p1->_triangles; } if (edge_p2 != pNew) { for(TriangleSet::iterator itr=edge_p2->_triangles.begin(); itr!=edge_p2->_triangles.end(); ++itr) { if (edge->_triangles.count(*itr)==0) { Triangle* triangle = const_cast(itr->get()); triangles_p2.push_back(triangle); oldEdges.insert(triangle->_e1); oldEdges.insert(triangle->_e2); oldEdges.insert(triangle->_e3); } } //triangles_p2 = edge_p2->_triangles; } for(LocalEdgeList::iterator oeitr=oldEdges.begin(); oeitr!=oldEdges.end(); ++oeitr) { _edgeSet.erase(*oeitr); const_cast(oeitr->get())->setErrorMetric(0.0f); _edgeSet.insert(*oeitr); } TriangleList::iterator titr_p1, titr_p2; for(titr_p1 = triangles_p1.begin(); titr_p1 != triangles_p1.end(); ++titr_p1) { removeTriangle(const_cast(titr_p1->get())); } for(titr_p2 = triangles_p2.begin(); titr_p2 != triangles_p2.end(); ++titr_p2) { removeTriangle(const_cast(titr_p2->get())); } //OSG_NOTICE<<" pNew="<_triangles; for(TriangleSet::iterator teitr=trianglesToRemove.begin(); teitr!=trianglesToRemove.end(); ++teitr) { Triangle* triangle = const_cast(teitr->get()); removeTriangle(triangle); } LocalEdgeList newEdges; for(titr_p1 = triangles_p1.begin(); titr_p1 != triangles_p1.end(); ++titr_p1) { Triangle* triangle = const_cast(titr_p1->get()); Point* p1 = (triangle->_p1==edge_p1 || triangle->_p1==edge_p2)? pNew : triangle->_p1.get(); Point* p2 = (triangle->_p2==edge_p1 || triangle->_p2==edge_p2)? pNew : triangle->_p2.get(); Point* p3 = (triangle->_p3==edge_p1 || triangle->_p3==edge_p2)? pNew : triangle->_p3.get(); Triangle* newTri = addTriangle(p1,p2,p3); if (newTri) { newEdges.insert(newTri->_e1); newEdges.insert(newTri->_e2); newEdges.insert(newTri->_e3); } } for(titr_p2 = triangles_p2.begin(); titr_p2 != triangles_p2.end(); ++titr_p2) { Triangle* triangle = const_cast(titr_p2->get()); Point* p1 = (triangle->_p1==edge_p1 || triangle->_p1==edge_p2)? pNew : triangle->_p1.get(); Point* p2 = (triangle->_p2==edge_p1 || triangle->_p2==edge_p2)? pNew : triangle->_p2.get(); Point* p3 = (triangle->_p3==edge_p1 || triangle->_p3==edge_p2)? pNew : triangle->_p3.get(); Triangle* newTri = addTriangle(p1,p2,p3); if (newTri) { newEdges.insert(newTri->_e1); newEdges.insert(newTri->_e2); newEdges.insert(newTri->_e3); } } LocalEdgeList edges2UpdateErrorMetric; LocalEdgeList::const_iterator newEdgeIt(newEdges.begin()); while (newEdgeIt != newEdges.end()) { const Point* p = 0; if (newEdgeIt->get()->_p1.get() != pNew) p = newEdgeIt->get()->_p1.get(); else p = newEdgeIt->get()->_p2.get(); TriangleSet::const_iterator triangleIt(p->_triangles.begin()); while (triangleIt != p->_triangles.end()) { const Triangle* triangle = triangleIt->get(); if (triangle->_e1->_p1 == p || triangle->_e1->_p2 == p) edges2UpdateErrorMetric.insert(triangle->_e1); if (triangle->_e2->_p1 == p || triangle->_e2->_p2 == p) edges2UpdateErrorMetric.insert(triangle->_e2); if (triangle->_e3->_p1 == p || triangle->_e3->_p2 == p) edges2UpdateErrorMetric.insert(triangle->_e3); ++triangleIt; } ++newEdgeIt; } edges2UpdateErrorMetric.insert(newEdges.begin(), newEdges.end()); // OSG_NOTICE<<"Edges to recalibarate "<get()<<")"<(itr->get())); } return true; } bool divideEdge(Edge* edge, Point* pNew) { // OSG_NOTICE<<"divideEdge("< > LocalEdgeList; LocalEdgeList edges2UpdateErrorMetric; TriangleSet::iterator titr; // for each deleted triangle insert two new triangles for(titr = triangles.begin(); titr != triangles.end(); ++titr) { Triangle* tri = const_cast(titr->get()); int edgeToReplace = 0; if (edge->_p1 == tri->_p1) { if (edge->_p2 == tri->_p2.get()) edgeToReplace = 1; // edge p1,p2 else if (edge->_p2 == tri->_p3.get()) edgeToReplace = 3; // edge p3, p1 } else if (edge->_p1 == tri->_p2.get()) { if (edge->_p2 == tri->_p3.get()) edgeToReplace = 2; // edge p2,p3 else if (edge->_p2 == tri->_p1.get()) edgeToReplace = 1; // edge p1, p2 } else if (edge->_p1 == tri->_p3.get()) { if (edge->_p2 == tri->_p1.get()) edgeToReplace = 3; // edge p3,p1 else if (edge->_p2 == tri->_p2.get()) edgeToReplace = 2; // edge p2, p3 } Triangle* newTri1 = 0; Triangle* newTri2 = 0; switch(edgeToReplace) { case(0): // error, shouldn't get here. OSG_NOTICE<<"Error EdgeCollapse::divideEdge(Edge*,Point*) passed invalid edge."<_p1.get(), pNew, tri->_p3.get());"<_p1.get(), pNew, tri->_p3.get()); // OSG_NOTICE<<" newTri2 = addTriangle(pNew, tri->_p2.get(), tri->_p3.get());"<_p2.get(), tri->_p3.get()); break; case(2): // p2, p3 // OSG_NOTICE<<" // p2, p3"<_p1.get(), tri->_p2.get(), pNew);"<_p1.get(), tri->_p2.get(), pNew); //OSG_NOTICE<<" newTri2 = addTriangle(tri->_p1.get(), pNew, tri->_p3.get());"<_p1.get(), pNew, tri->_p3.get()); break; case(3): // p3, p1 // OSG_NOTICE<<" // p3, p1"<_p1.get(), tri->_p2.get(), pNew);"<_p1.get(), tri->_p2.get(), pNew); // OSG_NOTICE<<" newTri2 = addTriangle(pNew, tri->_p2.get(), tri->_p3.get());"<_p2.get(), tri->_p3.get()); break; } if (newTri1) { edges2UpdateErrorMetric.insert(newTri1->_e1.get()); edges2UpdateErrorMetric.insert(newTri1->_e2.get()); edges2UpdateErrorMetric.insert(newTri1->_e3.get()); } if (newTri2) { edges2UpdateErrorMetric.insert(newTri2->_e1.get()); edges2UpdateErrorMetric.insert(newTri2->_e2.get()); edges2UpdateErrorMetric.insert(newTri2->_e3.get()); } } // unsigned int numTriangles2 = _triangleSet.size(); // unsigned int numEdges2 = _edgeSet.size(); // unsigned int numBoundaryEdges2 = computeNumBoundaryEdges(); // remove all the triangles associated with edge for(titr = triangles.begin(); titr != triangles.end(); ++titr) { removeTriangle(const_cast(titr->get())); } for(LocalEdgeList::iterator itr=edges2UpdateErrorMetric.begin(); itr!=edges2UpdateErrorMetric.end(); ++itr) { //OSG_NOTICE<<"updateErrorMetricForEdge("<get()<<")"<valid()) updateErrorMetricForEdge(const_cast(itr->get())); } // unsigned int numBoundaryEdges3 = computeNumBoundaryEdges(); // unsigned int numEdges3 = _edgeSet.size(); // unsigned int numTriangles3 = _triangleSet.size(); // OSG_NOTICE<<" numTriangles1="<_e1=="<_e1.get()<_e2=="<_e2.get()<_e3=="<_e3.get()<_triangles.empty()) { OSG_NOTICE<<"testEdge("<(itr->get())); } return numErrors; } unsigned int computeNumBoundaryEdges() { unsigned int numBoundaryEdges = 0; for(EdgeSet::iterator itr = _edgeSet.begin(); itr!=_edgeSet.end(); ++itr) { if ((*itr)->isBoundaryEdge()) ++numBoundaryEdges; } return numBoundaryEdges; } Point* addPoint(Triangle* triangle, unsigned int p1) { return addPoint(triangle,_originalPointList[p1].get()); } Point* addPoint(Triangle* triangle, Point* point) { PointSet::iterator itr = _pointSet.find(point); if (itr==_pointSet.end()) { //OSG_NOTICE<<" addPoint("<(itr->get()); //OSG_NOTICE<<" reusePoint("<_triangles.insert(triangle); return point; } void removePoint(Triangle* triangle, Point* point) { PointSet::iterator itr = _pointSet.find(point); if (itr!=_pointSet.end()) { point->_triangles.erase(triangle); if (point->_triangles.empty()) { // point no longer in use, so need to delete. _pointSet.erase(itr); } } } unsigned int testPoint(Point* point) { unsigned int numErrors = 0; for(TriangleSet::iterator itr=point->_triangles.begin(); itr!=point->_triangles.end(); ++itr) { Triangle* triangle = const_cast(itr->get()); if (!(triangle->_p1 == point || triangle->_p2 == point || triangle->_p3 == point)) { OSG_NOTICE<<"testPoint("<_p1 "<_p1.get()<_p2 "<_p2.get()<_p3 "<_p3.get()<(itr->get())); } return numErrors; } //protected: typedef std::vector< osg::ref_ptr > ArrayList; osg::Geometry* _geometry; bool _computeErrorMetricUsingLength; EdgeSet _edgeSet; TriangleSet _triangleSet; PointSet _pointSet; PointList _originalPointList; }; struct CollectTriangleOperator { CollectTriangleOperator():_ec(0) {} void setEdgeCollapse(EdgeCollapse* ec) { _ec = ec; } EdgeCollapse* _ec; // for use in the triangle functor. inline void operator()(unsigned int p1, unsigned int p2, unsigned int p3) { _ec->addTriangle(p1,p2,p3); } }; EdgeCollapse::~EdgeCollapse() { std::for_each(_edgeSet.begin(),_edgeSet.end(),dereference_clear()); std::for_each(_triangleSet.begin(),_triangleSet.end(),dereference_clear()); std::for_each(_pointSet.begin(),_pointSet.end(),dereference_clear()); std::for_each(_originalPointList.begin(),_originalPointList.end(),dereference_clear()); } typedef osg::TriangleIndexFunctor CollectTriangleIndexFunctor; class CopyArrayToPointsVisitor : public osg::ArrayVisitor { public: CopyArrayToPointsVisitor(EdgeCollapse::PointList& pointList): _pointList(pointList) {} template void copy(T& array) { if (_pointList.size()!=array.size()) return; for(unsigned int i=0;i<_pointList.size();++i) _pointList[i]->_attributes.push_back((float)array[i]); } virtual void apply(osg::Array&) {} virtual void apply(osg::ByteArray& array) { copy(array); } virtual void apply(osg::ShortArray& array) { copy(array); } virtual void apply(osg::IntArray& array) { copy(array); } virtual void apply(osg::UByteArray& array) { copy(array); } virtual void apply(osg::UShortArray& array) { copy(array); } virtual void apply(osg::UIntArray& array) { copy(array); } virtual void apply(osg::FloatArray& array) { copy(array); } virtual void apply(osg::Vec4ubArray& array) { if (_pointList.size()!=array.size()) return; for(unsigned int i=0;i<_pointList.size();++i) { osg::Vec4ub& value = array[i]; EdgeCollapse::FloatList& attributes = _pointList[i]->_attributes; attributes.push_back((float)value.r()); attributes.push_back((float)value.g()); attributes.push_back((float)value.b()); attributes.push_back((float)value.a()); } } virtual void apply(osg::Vec2Array& array) { if (_pointList.size()!=array.size()) return; for(unsigned int i=0;i<_pointList.size();++i) { osg::Vec2& value = array[i]; EdgeCollapse::FloatList& attributes = _pointList[i]->_attributes; attributes.push_back(value.x()); attributes.push_back(value.y()); } } virtual void apply(osg::Vec3Array& array) { if (_pointList.size()!=array.size()) return; for(unsigned int i=0;i<_pointList.size();++i) { osg::Vec3& value = array[i]; EdgeCollapse::FloatList& attributes = _pointList[i]->_attributes; attributes.push_back(value.x()); attributes.push_back(value.y()); attributes.push_back(value.z()); } } virtual void apply(osg::Vec4Array& array) { if (_pointList.size()!=array.size()) return; for(unsigned int i=0;i<_pointList.size();++i) { osg::Vec4& value = array[i]; EdgeCollapse::FloatList& attributes = _pointList[i]->_attributes; attributes.push_back(value.x()); attributes.push_back(value.y()); attributes.push_back(value.z()); attributes.push_back(value.w()); } } EdgeCollapse::PointList& _pointList; protected: CopyArrayToPointsVisitor& operator = (const CopyArrayToPointsVisitor&) { return *this; } }; class CopyVertexArrayToPointsVisitor : public osg::ArrayVisitor { public: CopyVertexArrayToPointsVisitor(EdgeCollapse::PointList& pointList): _pointList(pointList) {} virtual void apply(osg::Vec2Array& array) { if (_pointList.size()!=array.size()) return; for(unsigned int i=0;i<_pointList.size();++i) { _pointList[i] = new EdgeCollapse::Point; _pointList[i]->_index = i; osg::Vec2& value = array[i]; osg::Vec3& vertex = _pointList[i]->_vertex; vertex.set(value.x(),value.y(),0.0f); } } virtual void apply(osg::Vec3Array& array) { if (_pointList.size()!=array.size()) return; for(unsigned int i=0;i<_pointList.size();++i) { _pointList[i] = new EdgeCollapse::Point; _pointList[i]->_index = i; _pointList[i]->_vertex = array[i]; } } virtual void apply(osg::Vec4Array& array) { if (_pointList.size()!=array.size()) return; for(unsigned int i=0;i<_pointList.size();++i) { _pointList[i] = new EdgeCollapse::Point; _pointList[i]->_index = i; osg::Vec4& value = array[i]; osg::Vec3& vertex = _pointList[i]->_vertex; vertex.set(value.x()/value.w(),value.y()/value.w(),value.z()/value.w()); } } EdgeCollapse::PointList& _pointList; protected: CopyVertexArrayToPointsVisitor& operator = (const CopyVertexArrayToPointsVisitor&) { return *this; } }; void EdgeCollapse::setGeometry(osg::Geometry* geometry, const Simplifier::IndexList& protectedPoints) { _geometry = geometry; // check to see if vertex attributes indices exists, if so expand them to remove them if (_geometry->containsSharedArrays()) { // removing coord indices OSG_INFO<<"EdgeCollapse::setGeometry(..): Duplicate shared arrays"<duplicateSharedArrays(); } unsigned int numVertices = geometry->getVertexArray()->getNumElements(); _originalPointList.resize(numVertices); // copy vertices across to local point list CopyVertexArrayToPointsVisitor copyVertexArrayToPoints(_originalPointList); _geometry->getVertexArray()->accept(copyVertexArrayToPoints); // copy other per vertex attributes across to local point list. CopyArrayToPointsVisitor copyArrayToPoints(_originalPointList); for(unsigned int ti=0;ti<_geometry->getNumTexCoordArrays();++ti) { if (_geometry->getTexCoordArray(ti)) geometry->getTexCoordArray(ti)->accept(copyArrayToPoints); } if (_geometry->getNormalArray() && _geometry->getNormalArray()->getBinding()==osg::Array::BIND_PER_VERTEX) geometry->getNormalArray()->accept(copyArrayToPoints); if (_geometry->getColorArray() && _geometry->getColorArray()->getBinding()==osg::Array::BIND_PER_VERTEX) geometry->getColorArray()->accept(copyArrayToPoints); if (_geometry->getSecondaryColorArray() && _geometry->getSecondaryColorArray()->getBinding()==osg::Array::BIND_PER_VERTEX) geometry->getSecondaryColorArray()->accept(copyArrayToPoints); if (_geometry->getFogCoordArray() && _geometry->getFogCoordArray()->getBinding()==osg::Array::BIND_PER_VERTEX) geometry->getFogCoordArray()->accept(copyArrayToPoints); for(unsigned int vi=0;vi<_geometry->getNumVertexAttribArrays();++vi) { if (_geometry->getVertexAttribArray(vi) && _geometry->getVertexAttribArray(vi)->getBinding()==osg::Array::BIND_PER_VERTEX) geometry->getVertexAttribArray(vi)->accept(copyArrayToPoints); } // now set the protected points up. for(Simplifier::IndexList::const_iterator pitr=protectedPoints.begin(); pitr!=protectedPoints.end(); ++pitr) { _originalPointList[*pitr]->_protected = true; } CollectTriangleIndexFunctor collectTriangles; collectTriangles.setEdgeCollapse(this); _geometry->accept(collectTriangles); } class CopyPointsToArrayVisitor : public osg::ArrayVisitor { public: CopyPointsToArrayVisitor(EdgeCollapse::PointList& pointList): _pointList(pointList), _index(0) {} template void copy(T& array, R /*dummy*/) { array.resize(_pointList.size()); for(unsigned int i=0;i<_pointList.size();++i) { if (_index<_pointList[i]->_attributes.size()) { float val = (_pointList[i]->_attributes[_index]); array[i] = R (val); } } ++_index; } // use local typedefs if usinged char,short and int to get round gcc 3.3.1 problem with defining unsigned short() typedef unsigned char dummy_uchar; typedef unsigned short dummy_ushort; typedef unsigned int dummy_uint; virtual void apply(osg::Array&) {} virtual void apply(osg::ByteArray& array) { copy(array, char());} virtual void apply(osg::ShortArray& array) { copy(array, short()); } virtual void apply(osg::IntArray& array) { copy(array, int()); } virtual void apply(osg::UByteArray& array) { copy(array, dummy_uchar()); } virtual void apply(osg::UShortArray& array) { copy(array,dummy_ushort()); } virtual void apply(osg::UIntArray& array) { copy(array, dummy_uint()); } virtual void apply(osg::FloatArray& array) { copy(array, float()); } virtual void apply(osg::Vec4ubArray& array) { array.resize(_pointList.size()); for(unsigned int i=0;i<_pointList.size();++i) { EdgeCollapse::FloatList& attributes = _pointList[i]->_attributes; array[i].set((unsigned char)attributes[_index], (unsigned char)attributes[_index+1], (unsigned char)attributes[_index+2], (unsigned char)attributes[_index+3]); } _index += 4; } virtual void apply(osg::Vec2Array& array) { array.resize(_pointList.size()); for(unsigned int i=0;i<_pointList.size();++i) { EdgeCollapse::FloatList& attributes = _pointList[i]->_attributes; if (_index+1_attributes; if (_index+2_attributes; if (_index+3 void normalize(Itr begin, Itr end) { for(Itr itr = begin; itr != end; ++itr) { itr->normalize(); } } virtual void apply(osg::Vec2Array& array) { normalize(array.begin(),array.end()); } virtual void apply(osg::Vec3Array& array) { normalize(array.begin(),array.end()); } virtual void apply(osg::Vec4Array& array) { normalize(array.begin(),array.end()); } }; class CopyPointsToVertexArrayVisitor : public osg::ArrayVisitor { public: CopyPointsToVertexArrayVisitor(EdgeCollapse::PointList& pointList): _pointList(pointList) {} virtual void apply(osg::Vec2Array& array) { array.resize(_pointList.size()); for(unsigned int i=0;i<_pointList.size();++i) { _pointList[i]->_index = i; osg::Vec3& vertex = _pointList[i]->_vertex; array[i].set(vertex.x(),vertex.y()); } } virtual void apply(osg::Vec3Array& array) { array.resize(_pointList.size()); for(unsigned int i=0;i<_pointList.size();++i) { _pointList[i]->_index = i; array[i] = _pointList[i]->_vertex; } } virtual void apply(osg::Vec4Array& array) { array.resize(_pointList.size()); for(unsigned int i=0;i<_pointList.size();++i) { _pointList[i]->_index = i; osg::Vec3& vertex = _pointList[i]->_vertex; array[i].set(vertex.x(),vertex.y(),vertex.z(),1.0f); } } EdgeCollapse::PointList& _pointList; protected: CopyPointsToVertexArrayVisitor& operator = (const CopyPointsToVertexArrayVisitor&) { return *this; } }; void EdgeCollapse::copyBackToGeometry() { // rebuild the _pointList from the _pointSet _originalPointList.clear(); std::copy(_pointSet.begin(),_pointSet.end(),std::back_inserter(_originalPointList)); // copy vertices across to local point list CopyPointsToVertexArrayVisitor copyVertexArrayToPoints(_originalPointList); _geometry->getVertexArray()->accept(copyVertexArrayToPoints); // copy other per vertex attributes across to local point list. CopyPointsToArrayVisitor copyArrayToPoints(_originalPointList); for(unsigned int ti=0;ti<_geometry->getNumTexCoordArrays();++ti) { if (_geometry->getTexCoordArray(ti)) _geometry->getTexCoordArray(ti)->accept(copyArrayToPoints); } if (_geometry->getNormalArray() && _geometry->getNormalArray()->getBinding()==osg::Array::BIND_PER_VERTEX) { _geometry->getNormalArray()->accept(copyArrayToPoints); // now normalize the normals. NormalizeArrayVisitor nav; _geometry->getNormalArray()->accept(nav); } if (_geometry->getColorArray() && _geometry->getColorArray()->getBinding()==osg::Array::BIND_PER_VERTEX) _geometry->getColorArray()->accept(copyArrayToPoints); if (_geometry->getSecondaryColorArray() && _geometry->getSecondaryColorArray()->getBinding()==osg::Array::BIND_PER_VERTEX) _geometry->getSecondaryColorArray()->accept(copyArrayToPoints); if (_geometry->getFogCoordArray() && _geometry->getFogCoordArray()->getBinding()==osg::Array::BIND_PER_VERTEX) _geometry->getFogCoordArray()->accept(copyArrayToPoints); for(unsigned int vi=0;vi<_geometry->getNumVertexAttribArrays();++vi) { if (_geometry->getVertexAttribArray(vi) && _geometry->getVertexAttribArray(vi)->getBinding()==osg::Array::BIND_PER_VERTEX) _geometry->getVertexAttribArray(vi)->accept(copyArrayToPoints); } typedef std::set< osg::ref_ptr, dereference_less > TrianglesSorted; TrianglesSorted trianglesSorted; for(TriangleSet::iterator itr = _triangleSet.begin(); itr != _triangleSet.end(); ++itr) { trianglesSorted.insert(*itr); } osg::DrawElementsUInt* primitives = new osg::DrawElementsUInt(GL_TRIANGLES,trianglesSorted.size()*3); unsigned int pos = 0; for(TrianglesSorted::iterator titr=trianglesSorted.begin(); titr!=trianglesSorted.end(); ++titr) { const Triangle* triangle = (*titr).get(); (*primitives)[pos++] = triangle->_p1->_index; (*primitives)[pos++] = triangle->_p2->_index; (*primitives)[pos++] = triangle->_p3->_index; } _geometry->getPrimitiveSetList().clear(); _geometry->addPrimitiveSet(primitives); } Simplifier::Simplifier(double sampleRatio, double maximumError, double maximumLength): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), _sampleRatio(sampleRatio), _maximumError(maximumError), _maximumLength(maximumLength), _triStrip(true), _smoothing(true) { } void Simplifier::simplify(osg::Geometry& geometry) { // pass an empty list of indices to simply(Geometry,IndexList) // so that this one method handle both cases of non protected indices // and specified indices. IndexList emptyList; simplify(geometry,emptyList); } void Simplifier::simplify(osg::Geometry& geometry, const IndexList& protectedPoints) { OSG_INFO<<"++++++++++++++simplifier************"<getErrorMetric() , numOriginalPrimitives, ec._triangleSet.size()) && ec.collapseMinimumErrorEdge()) { //OSG_INFO<<" Collapsed edge ec._triangleSet.size()="<getErrorMetric() , numOriginalPrimitives, ec._triangleSet.size()) && // ec._triangleSet.size() < targetNumTriangles && ec.divideLongestEdge()) { //OSG_INFO<<" Edge divided ec._triangleSet.size()="<