OpenSceneGraph/src/osgUtil/SmoothingVisitor.cpp

699 lines
20 KiB
C++

/* -*-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 <osg/TriangleFunctor>
#include <osg/TriangleIndexFunctor>
#include <osg/io_utils>
#include <osgUtil/SmoothingVisitor>
#include <stdio.h>
#include <list>
#include <set>
using namespace osg;
using namespace osgUtil;
namespace Smoother
{
struct LessPtr
{
inline bool operator() (const osg::Vec3* lhs,const osg::Vec3* rhs) const
{
return *lhs<*rhs;
}
};
// triangle functor.
struct SmoothTriangleFunctor
{
osg::Vec3* _coordBase;
osg::Vec3* _normalBase;
typedef std::multiset<const osg::Vec3*,LessPtr> CoordinateSet;
CoordinateSet _coordSet;
SmoothTriangleFunctor():
_coordBase(0),
_normalBase(0) {}
void set(osg::Vec3 *cb,int noVertices, osg::Vec3 *nb)
{
_coordBase=cb;
_normalBase=nb;
osg::Vec3* vptr = cb;
for(int i=0;i<noVertices;++i)
{
_coordSet.insert(vptr++);
}
}
inline void updateNormal(const osg::Vec3& normal,const osg::Vec3* vptr)
{
std::pair<CoordinateSet::iterator,CoordinateSet::iterator> p =
_coordSet.equal_range(vptr);
for(CoordinateSet::iterator itr=p.first;
itr!=p.second;
++itr)
{
osg::Vec3* nptr = _normalBase + (*itr-_coordBase);
(*nptr) += normal;
}
}
inline void operator() ( const osg::Vec3 &v1, const osg::Vec3 &v2, const osg::Vec3 &v3, bool treatVertexDataAsTemporary )
{
if (!treatVertexDataAsTemporary)
{
// calc orientation of triangle.
osg::Vec3 normal = (v2-v1)^(v3-v1);
// normal.normalize();
updateNormal(normal,&v1);
updateNormal(normal,&v2);
updateNormal(normal,&v3);
}
}
};
static void smooth_old(osg::Geometry& geom)
{
OSG_INFO<<"smooth_old("<<&geom<<")"<<std::endl;
Geometry::PrimitiveSetList& primitives = geom.getPrimitiveSetList();
Geometry::PrimitiveSetList::iterator itr;
unsigned int numSurfacePrimitives=0;
for(itr=primitives.begin();
itr!=primitives.end();
++itr)
{
switch((*itr)->getMode())
{
case(PrimitiveSet::TRIANGLES):
case(PrimitiveSet::TRIANGLE_STRIP):
case(PrimitiveSet::TRIANGLE_FAN):
case(PrimitiveSet::QUADS):
case(PrimitiveSet::QUAD_STRIP):
case(PrimitiveSet::POLYGON):
++numSurfacePrimitives;
break;
default:
break;
}
}
if (!numSurfacePrimitives) return;
osg::Vec3Array *coords = dynamic_cast<osg::Vec3Array*>(geom.getVertexArray());
if (!coords || !coords->size()) return;
osg::Vec3Array *normals = new osg::Vec3Array(coords->size());
osg::Vec3Array::iterator nitr;
for(nitr = normals->begin();
nitr!=normals->end();
++nitr)
{
nitr->set(0.0f,0.0f,0.0f);
}
TriangleFunctor<SmoothTriangleFunctor> stf;
stf.set(&(coords->front()),coords->size(),&(normals->front()));
geom.accept(stf);
for(nitr= normals->begin();
nitr!=normals->end();
++nitr)
{
nitr->normalize();
}
geom.setNormalArray( normals, osg::Array::BIND_PER_VERTEX);
geom.dirtyDisplayList();
}
struct SmoothTriangleIndexFunctor
{
SmoothTriangleIndexFunctor():
_vertices(0),
_normals(0)
{
}
bool set(osg::Vec3Array* vertices, osg::Vec3Array* normals)
{
_vertices = vertices;
_normals = normals;
if (!_vertices)
{
OSG_NOTICE<<"Warning: SmoothTriangleIndexFunctor::set(..) requires a valid vertex arrays."<<std::endl;
return false;
}
if (!_normals)
{
OSG_NOTICE<<"Warning: SmoothTriangleIndexFunctor::set(..) requires a valid normal arrays."<<std::endl;
return false;
}
for(osg::Vec3Array::iterator itr = _normals->begin();
itr != _normals->end();
++itr)
{
(*itr).set(0.0f,0.0f,0.0f);
}
return true;
}
void normalize()
{
if (!_normals) return;
for(osg::Vec3Array::iterator itr = _normals->begin();
itr != _normals->end();
++itr)
{
(*itr).normalize();
}
}
void operator() (unsigned int p1, unsigned int p2, unsigned int p3)
{
if (p1==p2 || p2==p3 || p1==p3)
{
return;
}
const osg::Vec3& v1 = (*_vertices)[p1];
const osg::Vec3& v2 = (*_vertices)[p2];
const osg::Vec3& v3 = (*_vertices)[p3];
osg::Vec3 normal( (v2-v1)^(v3-v1) );
normal.normalize();
(*_normals)[p1] += normal;
(*_normals)[p2] += normal;
(*_normals)[p3] += normal;
}
osg::Vec3Array* _vertices;
osg::Vec3Array* _normals;
};
struct FindSharpEdgesFunctor
{
FindSharpEdgesFunctor():
_vertices(0),
_normals(0),
_maxDeviationDotProduct(0.0f),
_currentPrimitiveSetIndex(0)
{
}
struct Triangle : public osg::Referenced
{
Triangle(unsigned int primitiveSetIndex, unsigned int p1, unsigned int p2, unsigned int p3):
_primitiveSetIndex(primitiveSetIndex), _p1(p1), _p2(p2), _p3(p3) {}
Triangle(const Triangle& tri):
osg::Referenced(true),
_primitiveSetIndex(tri._primitiveSetIndex), _p1(tri._p1), _p2(tri._p2), _p3(tri._p3) {}
Triangle& operator = (const Triangle& tri)
{
_primitiveSetIndex = tri._primitiveSetIndex;
_p1 = tri._p1;
_p2 = tri._p2;
_p3 = tri._p3;
return *this;
}
unsigned int _primitiveSetIndex;
unsigned int _p1;
unsigned int _p2;
unsigned int _p3;
};
typedef std::list< osg::ref_ptr<Triangle> > Triangles;
struct ProblemVertex : public osg::Referenced
{
ProblemVertex(unsigned int p):
_point(p) {}
unsigned int _point;
Triangles _triangles;
};
typedef std::vector< osg::ref_ptr<ProblemVertex> > ProblemVertexVector;
typedef std::list< osg::ref_ptr<ProblemVertex> > ProblemVertexList;
typedef std::list< osg::ref_ptr<osg::Array> > ArrayList;
bool set(osg::Geometry* geom, float creaseAngle)
{
_geometry = geom;
_creaseAngle = creaseAngle;
if (!_geometry)
{
OSG_NOTICE<<"Warning: SmoothTriangleIndexFunctor::set(..) requires a geometry."<<std::endl;
return false;
}
_vertices = dynamic_cast<osg::Vec3Array*>(_geometry->getVertexArray());
_normals = dynamic_cast<osg::Vec3Array*>(_geometry->getNormalArray());
_maxDeviationDotProduct = cos(_creaseAngle*0.5);
if (!_vertices)
{
OSG_NOTICE<<"Warning: SmoothTriangleIndexFunctor::set(..) requires a valid vertex arrays."<<std::endl;
return false;
}
if (!_normals)
{
OSG_NOTICE<<"Warning: SmoothTriangleIndexFunctor::set(..) requires a valid normal arrays."<<std::endl;
return false;
}
_problemVertexVector.resize(_vertices->size());
addArray(geom->getVertexArray());
addArray(geom->getNormalArray());
addArray(geom->getColorArray());
addArray(geom->getSecondaryColorArray());
addArray(geom->getFogCoordArray());
for(unsigned int i=0; i<geom->getNumTexCoordArrays(); ++i)
{
addArray(geom->getTexCoordArray(i));
}
return true;
}
void addArray(osg::Array* array)
{
if (array && array->getBinding()==osg::Array::BIND_PER_VERTEX)
{
_arrays.push_back(array);
}
}
void operator() (unsigned int p1, unsigned int p2, unsigned int p3)
{
osg::Vec3 normal( computeNormal(p1, p2, p3) );
if (p1==p2 || p2==p3 || p1==p3)
{
// OSG_NOTICE<<"NULL triangle ("<<p1<<", "<<p2<<", "<<p3<<")"<<std::endl;
return;
}
Triangle* tri = new Triangle(_currentPrimitiveSetIndex, p1, p2, p3);
_triangles.push_back(tri);
if (checkDeviation(p1, normal)) markProblemVertex(p1);
if (checkDeviation(p2, normal)) markProblemVertex(p2);
if (checkDeviation(p3, normal)) markProblemVertex(p3);
}
void markProblemVertex(unsigned int p)
{
if (!_problemVertexVector[p])
{
_problemVertexVector[p] = new ProblemVertex(p);
_problemVertexList.push_back(_problemVertexVector[p]);
}
}
void checkTrianglesForProblemVertices()
{
for(Triangles::iterator itr = _triangles.begin();
itr != _triangles.end();
++itr)
{
Triangle* tri = itr->get();
insertTriangleIfProblemVertex(tri->_p1, tri);
insertTriangleIfProblemVertex(tri->_p2, tri);
insertTriangleIfProblemVertex(tri->_p3, tri);
}
}
void insertTriangleIfProblemVertex(unsigned int p, Triangle* tri)
{
if (_problemVertexVector[p]) _problemVertexVector[p]->_triangles.push_back(tri);
}
bool checkDeviation(unsigned int p, osg::Vec3& normal)
{
float deviation = normal * (*_normals)[p];
return (deviation < _maxDeviationDotProduct);
}
osg::Vec3 computeNormal(unsigned int p1, unsigned int p2, unsigned int p3)
{
const osg::Vec3& v1 = (*_vertices)[p1];
const osg::Vec3& v2 = (*_vertices)[p2];
const osg::Vec3& v3 = (*_vertices)[p3];
osg::Vec3 normal( (v2-v1)^(v3-v1) );
normal.normalize();
return normal;
}
void listProblemVertices()
{
OSG_NOTICE<<"listProblemVertices() "<<_problemVertexList.size()<<std::endl;
for(ProblemVertexList::iterator itr = _problemVertexList.begin();
itr != _problemVertexList.end();
++itr)
{
ProblemVertex* pv = itr->get();
OSG_NOTICE<<" pv._point = "<<pv->_point<<" triangles "<<pv->_triangles.size()<<std::endl;
for(Triangles::iterator titr = pv->_triangles.begin();
titr != pv->_triangles.end();
++titr)
{
Triangle* tri = titr->get();
OSG_NOTICE<<" triangle("<<tri->_p1<<", "<<tri->_p2<<", "<<tri->_p3<<")"<<std::endl;
osg::Vec3 normal( computeNormal(tri->_p1, tri->_p2, tri->_p3) );
float deviation = normal * (*_normals)[pv->_point];
OSG_NOTICE<<" deviation "<<deviation<<std::endl;
}
}
}
class DuplicateVertex : public osg::ArrayVisitor
{
public:
unsigned int _i;
unsigned int _end;
DuplicateVertex(unsigned int i):
_i(i),
_end(i) {}
template <class ARRAY>
void apply_imp(ARRAY& array)
{
_end = array.size();
array.push_back(array[_i]);
}
virtual void apply(osg::ByteArray& ba) { apply_imp(ba); }
virtual void apply(osg::ShortArray& ba) { apply_imp(ba); }
virtual void apply(osg::IntArray& ba) { apply_imp(ba); }
virtual void apply(osg::UByteArray& ba) { apply_imp(ba); }
virtual void apply(osg::UShortArray& ba) { apply_imp(ba); }
virtual void apply(osg::UIntArray& ba) { apply_imp(ba); }
virtual void apply(osg::Vec4ubArray& ba) { apply_imp(ba); }
virtual void apply(osg::FloatArray& ba) { apply_imp(ba); }
virtual void apply(osg::Vec2Array& ba) { apply_imp(ba); }
virtual void apply(osg::Vec3Array& ba) { apply_imp(ba); }
virtual void apply(osg::Vec4Array& ba) { apply_imp(ba); }
};
unsigned int duplicateVertex(unsigned int i)
{
DuplicateVertex duplicate(i);
for(ArrayList::iterator aItr = _arrays.begin();
aItr != _arrays.end();
++aItr)
{
(*aItr)->accept(duplicate);
}
return duplicate._end;
}
void duplicateProblemVertexAll(ProblemVertex* pv)
{
unsigned int p = pv->_point;
Triangles::iterator titr = pv->_triangles.begin();
++titr;
for(;
titr != pv->_triangles.end();
++titr)
{
Triangle* tri = titr->get();
unsigned int duplicated_p = duplicateVertex(p);
if (tri->_p1==p) tri->_p1 = duplicated_p;
if (tri->_p2==p) tri->_p2 = duplicated_p;
if (tri->_p3==p) tri->_p3 = duplicated_p;
}
}
void duplicateProblemVertex(ProblemVertex* pv)
{
if (pv->_triangles.size()<=2)
{
duplicateProblemVertexAll(pv);
}
else
{
// implement a form of greedy association based on similar orientation
// rather than iterating through all the various permutation of triangles that might
// provide the best fit.
unsigned int p = pv->_point;
Triangles::iterator titr = pv->_triangles.begin();
while(titr != pv->_triangles.end())
{
Triangle* tri = titr->get();
osg::Vec3 normal = computeNormal(tri->_p1, tri->_p2, tri->_p3);
Triangles associatedTriangles;
associatedTriangles.push_back(tri);
// remove triangle for list
pv->_triangles.erase(titr);
// reset iterator
titr = pv->_triangles.begin();
while(titr != pv->_triangles.end())
{
Triangle* tri2 = titr->get();
osg::Vec3 normal2 = computeNormal(tri2->_p1, tri2->_p2, tri2->_p3);
float deviation = normal * normal2;
if (deviation >= _maxDeviationDotProduct)
{
// Tri and tri2 are close enough together to associate.
associatedTriangles.push_back(tri2);
Triangles::iterator titr_to_erase = titr;
++titr;
pv->_triangles.erase(titr_to_erase);
}
else
{
++titr;
}
}
// create duplicate vertex to set of associated triangles
unsigned int duplicated_p = duplicateVertex(p);
// now rest the index on th triangles of the point that was duplicated
for(Triangles::iterator aitr = associatedTriangles.begin();
aitr != associatedTriangles.end();
++aitr)
{
Triangle* tri = aitr->get();
if (tri->_p1==p) tri->_p1 = duplicated_p;
if (tri->_p2==p) tri->_p2 = duplicated_p;
if (tri->_p3==p) tri->_p3 = duplicated_p;
}
// reset iterator to beginning
titr = pv->_triangles.begin();
}
}
}
void duplicateProblemVertices()
{
checkTrianglesForProblemVertices();
for(ProblemVertexList::iterator itr = _problemVertexList.begin();
itr != _problemVertexList.end();
++itr)
{
ProblemVertex* pv = itr->get();
if (pv->_triangles.size()>1)
{
duplicateProblemVertex(itr->get());
}
}
}
osg::PrimitiveSet* createPrimitiveSet(Triangles& triangles)
{
osg::ref_ptr<osg::DrawElements> elements = (_vertices->size()<16384) ?
static_cast<osg::DrawElements*>(new osg::DrawElementsUShort(GL_TRIANGLES)) :
static_cast<osg::DrawElements*>(new osg::DrawElementsUInt(GL_TRIANGLES));
elements->reserveElements(triangles.size()*3);
for(Triangles::iterator itr = triangles.begin();
itr != triangles.end();
++itr)
{
Triangle* tri = itr->get();
elements->addElement(tri->_p1);
elements->addElement(tri->_p2);
elements->addElement(tri->_p3);
}
return elements.release();
}
void updateGeometry()
{
duplicateProblemVertices();
typedef std::map<unsigned int, Triangles> TriangleMap;
TriangleMap triangleMap;
for(Triangles::iterator itr = _triangles.begin();
itr != _triangles.end();
++itr)
{
Triangle* tri = itr->get();
triangleMap[tri->_primitiveSetIndex].push_back(tri);
}
for(TriangleMap::iterator itr = triangleMap.begin();
itr != triangleMap.end();
++itr)
{
osg::PrimitiveSet* originalPrimitiveSet = _geometry->getPrimitiveSet(itr->first);
osg::PrimitiveSet* newPrimitiveSet = createPrimitiveSet(itr->second);
newPrimitiveSet->setName(originalPrimitiveSet->getName());
_geometry->setPrimitiveSet(itr->first, newPrimitiveSet);
}
}
osg::Geometry* _geometry;
osg::Vec3Array* _vertices;
osg::Vec3Array* _normals;
ArrayList _arrays;
float _creaseAngle;
float _maxDeviationDotProduct;
ProblemVertexVector _problemVertexVector;
ProblemVertexList _problemVertexList;
Triangles _triangles;
unsigned int _currentPrimitiveSetIndex;
};
static void smooth_new(osg::Geometry& geom, double creaseAngle)
{
OSG_INFO<<"smooth_new("<<&geom<<", "<<osg::RadiansToDegrees(creaseAngle)<<")"<<std::endl;
osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(geom.getVertexArray());
if (!vertices) return;
osg::Vec3Array* normals = dynamic_cast<osg::Vec3Array*>(geom.getNormalArray());
if (!normals || (normals && normals->size() != vertices->size()))
{
normals = new osg::Vec3Array(vertices->size());
geom.setNormalArray(normals, osg::Array::BIND_PER_VERTEX);
}
osg::TriangleIndexFunctor<SmoothTriangleIndexFunctor> stif;
if (stif.set(vertices, normals))
{
// accumulate all the normals
geom.accept(stif);
// normalize the normals
stif.normalize();
}
osg::TriangleIndexFunctor<FindSharpEdgesFunctor> fsef;
if (fsef.set(&geom, creaseAngle))
{
// look for normals that deviate too far
fsef.setVertexArray(vertices->getNumElements(), static_cast<const Vec3*>(vertices->getDataPointer()));
for(unsigned int i = 0; i < geom.getNumPrimitiveSets(); ++i)
{
fsef._currentPrimitiveSetIndex = i;
geom.getPrimitiveSet(i)->accept(fsef);
}
// fsef.listProblemVertices();
fsef.updateGeometry();
osg::TriangleIndexFunctor<SmoothTriangleIndexFunctor> stif2;
if (stif2.set(vertices, normals))
{
// accumulate all the normals
geom.accept(stif);
// normalize the normals
stif.normalize();
}
}
}
}
SmoothingVisitor::SmoothingVisitor():
_creaseAngle(osg::PI)
{
setTraversalMode(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN);
}
SmoothingVisitor::~SmoothingVisitor()
{
}
void SmoothingVisitor::smooth(osg::Geometry& geom, double creaseAngle)
{
if (creaseAngle==osg::PI)
{
Smoother::smooth_old(geom);
}
else
{
Smoother::smooth_new(geom, creaseAngle);
}
}
void SmoothingVisitor::apply(osg::Geode& geode)
{
for(unsigned int i = 0; i < geode.getNumDrawables(); i++ )
{
osg::Geometry* geom = dynamic_cast<osg::Geometry*>(geode.getDrawable(i));
if (geom) smooth(*geom, _creaseAngle);
}
}