Files
OpenSceneGraph/src/osgShadow/ConvexPolyhedron.cpp
Robert Osfield ba9dfb2ff6 From Albert Luaces, typo fixes.
git-svn-id: http://svn.openscenegraph.org/osg/OpenSceneGraph/trunk@14883 16af8721-9629-0410-8352-f15c8da7e697
2015-06-01 13:40:20 +00:00

1884 lines
61 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.
*
* ViewDependentShadow codes Copyright (C) 2008 Wojciech Lewandowski
* Thanks to to my company http://www.ai.com.pl for allowing me free this work.
*/
#include<osgShadow/ConvexPolyhedron>
#include<osg/Group>
#include<osg/Geode>
#include<osgDB/WriteFile>
#include<cassert>
#include<deque>
#include<algorithm>
#include<iterator>
#include <stdio.h>
#include <string.h>
using namespace osgShadow;
#if defined( DEBUG ) || defined( _DEBUG ) || defined( _DEBUG_ )
// ConvexPolyhedron may produce tons of warnings when it becomes non convex.
// Unfortuantely this condition often happens in daily routine of shadow usage
// due precision errors mixed with repeating frustum cuts performed by MinimalShadowClasses.
// However, in most of above cases this condition is not fatal
// because polyhedron becomes concave by very small margin (mesuring deep the hole).
// Unfortunately warnings are produced even for such small margin cases and can
// easily flood the console.
// So I leave MAKE_CHECKS commented out. Its really useful only for a guy who debugs
// larger concaveness issues which means most developers will want to keep it commented.
// #define MAKE_CHECKS 1
#endif
#if MAKE_CHECKS
#define WARN OSG_WARN
#define CONVEX_POLYHEDRON_CHECK_COHERENCY 1
#define CONVEX_POLYHEDRON_WARN_ON_INCOHERENT_DATA 2
#define CONVEX_POLYHEDRON_DUMP_ON_INCOHERENT_DATA 2
#define CONVEX_POLYHEDRON_WARN_ON_BAD_POLYGON 1
#define CONVEX_POLYHEDRON_DUMP_ON_BAD_POLYGON 1
#define CONVEX_POLYHEDRON_WARN_ON_CONCAVE_POLYGON 1
#define CONVEX_POLYHEDRON_DUMP_ON_CONCAVE_POLYGON 1
#define CONVEX_POLYHEDRON_WARN_ON_INCORRECT_FACE_CUT 1
#define CONVEX_POLYHEDRON_DUMP_ON_INCORRECT_FACE_CUT 1
#else
#define WARN OSG_WARN
#define CONVEX_POLYHEDRON_CHECK_COHERENCY 0
#define CONVEX_POLYHEDRON_WARN_ON_INCOHERENT_DATA 0
#define CONVEX_POLYHEDRON_DUMP_ON_INCOHERENT_DATA 0
#define CONVEX_POLYHEDRON_WARN_ON_BAD_POLYGON 0
#define CONVEX_POLYHEDRON_DUMP_ON_BAD_POLYGON 0
#define CONVEX_POLYHEDRON_WARN_ON_CONCAVE_POLYGON 0
#define CONVEX_POLYHEDRON_DUMP_ON_CONCAVE_POLYGON 0
#define CONVEX_POLYHEDRON_WARN_ON_INCORRECT_FACE_CUT 0
#define CONVEX_POLYHEDRON_DUMP_ON_INCORRECT_FACE_CUT 0
#endif
const osg::Matrix & ConvexPolyhedron::defaultMatrix = *(osg::Matrix*)NULL;
static const double epsi = pow( 2.0, -20.0 ); //circa 8 times float prec(~2^-23)
static const double plane_hull_tolerance = 1.0e-5;
static const double point_plane_tolerance = 0;
static const double point_point_tolerance = 0;
static const double point_point_equivalence = 0;
// Tim Moore modifications for GCC 4.3 August 15, 2008
// they correspond to Adrian Egli tweaks for VS 7.3 on August 19, 2008
namespace
{
typedef std::vector< double > Distances;
typedef std::vector< osg::Vec4d > Points;
// Auxiliary params continued per face
struct FaceDistances
{
ConvexPolyhedron::Faces::iterator itr;
Points points;
Distances distances;
int below, above, on;
};
} // namespace
ConvexPolyhedron::ConvexPolyhedron( const osg::Matrix& matrix,
const osg::Matrix& inverse,
const osg::BoundingBox& bb )
{
setToBoundingBox( bb );
if( &matrix != &defaultMatrix && &inverse != &defaultMatrix )
transform( matrix, inverse );
else if( &matrix != &defaultMatrix && &inverse == &defaultMatrix )
transform( matrix, osg::Matrix::inverse( matrix ) );
else if( &matrix == &defaultMatrix && &inverse != &defaultMatrix )
transform( osg::Matrix::inverse( inverse ), matrix );
}
void ConvexPolyhedron::setToUnitFrustum(bool withNear, bool withFar)
{
const osg::Vec3d v000(-1.0,-1.0,-1.0);
const osg::Vec3d v010(-1.0,1.0,-1.0);
const osg::Vec3d v001(-1.0,-1.0,1.0);
const osg::Vec3d v011(-1.0,1.0,1.0);
const osg::Vec3d v100(1.0,-1.0,-1.0);
const osg::Vec3d v110(1.0,1.0,-1.0);
const osg::Vec3d v101(1.0,-1.0,1.0);
const osg::Vec3d v111(1.0,1.0,1.0);
_faces.clear();
{ // left plane.
Face& face = createFace();
face.name = "left";
face.plane.set(1.0,0.0,0.0,1.0);
face.vertices.push_back(v000);
face.vertices.push_back(v001);
face.vertices.push_back(v011);
face.vertices.push_back(v010);
}
{ // right plane.
Face& face = createFace();
face.name = "right";
face.plane.set(-1.0,0.0,0.0,1.0);
face.vertices.push_back(v100);
face.vertices.push_back(v110);
face.vertices.push_back(v111);
face.vertices.push_back(v101);
}
{ // bottom plane.
Face& face = createFace();
face.name = "bottom";
face.plane.set(0.0,1.0,0.0,1.0);
face.vertices.push_back(v000);
face.vertices.push_back(v100);
face.vertices.push_back(v101);
face.vertices.push_back(v001);
}
{ // top plane.
Face& face = createFace();
face.name = "top";
face.plane.set(0.0,-1.0,0.0,1.0);
face.vertices.push_back(v010);
face.vertices.push_back(v011);
face.vertices.push_back(v111);
face.vertices.push_back(v110);
}
if (withNear)
{ // near plane
Face& face = createFace();
face.name = "near";
face.plane.set(0.0,0.0,1.0,1.0);
face.vertices.push_back(v000);
face.vertices.push_back(v010);
face.vertices.push_back(v110);
face.vertices.push_back(v100);
}
if (withFar)
{ // far plane
Face& face = createFace();
face.name = "far";
face.plane.set(0.0,0.0,-1.0,1.0);
face.vertices.push_back(v001);
face.vertices.push_back(v101);
face.vertices.push_back(v111);
face.vertices.push_back(v011);
}
}
void ConvexPolyhedron::setToBoundingBox(const osg::BoundingBox& bb)
{
_faces.clear();
// Ignore invalid and one dimensional boxes
if( bb._min[0] >= bb._max[0] ||
bb._min[1] >= bb._max[1] ||
bb._min[2] >= bb._max[2] ) return;
const osg::Vec3d v000(bb.xMin(),bb.yMin(), bb.zMin());
const osg::Vec3d v010(bb.xMin(),bb.yMax(), bb.zMin());
const osg::Vec3d v001(bb.xMin(),bb.yMin(), bb.zMax());
const osg::Vec3d v011(bb.xMin(),bb.yMax(), bb.zMax());
const osg::Vec3d v100(bb.xMax(),bb.yMin(), bb.zMin());
const osg::Vec3d v110(bb.xMax(),bb.yMax(), bb.zMin());
const osg::Vec3d v101(bb.xMax(),bb.yMin(), bb.zMax());
const osg::Vec3d v111(bb.xMax(),bb.yMax(), bb.zMax());
{ // x min plane
Face& face = createFace();
face.name = "xMin";
face.plane.set(1.0,0.0,0.0,-bb.xMin());
face.vertices.push_back(v000);
face.vertices.push_back(v001);
face.vertices.push_back(v011);
face.vertices.push_back(v010);
}
{ // x max plane.
Face& face = createFace();
face.name = "xMax";
face.plane.set(-1.0,0.0,0.0,bb.xMax());
face.vertices.push_back(v100);
face.vertices.push_back(v110);
face.vertices.push_back(v111);
face.vertices.push_back(v101);
}
{ // y min plane.
Face& face = createFace();
face.name = "yMin";
face.plane.set(0.0,1.0,0.0,-bb.yMin());
face.vertices.push_back(v000);
face.vertices.push_back(v100);
face.vertices.push_back(v101);
face.vertices.push_back(v001);
}
{ // y max plane.
Face& face = createFace();
face.name = "yMax";
face.plane.set(0.0,-1.0,0.0,bb.yMax());
face.vertices.push_back(v010);
face.vertices.push_back(v011);
face.vertices.push_back(v111);
face.vertices.push_back(v110);
}
{ // z min plane
Face& face = createFace();
face.name = "zMin";
face.plane.set(0.0,0.0,1.0,-bb.zMin());
face.vertices.push_back(v000);
face.vertices.push_back(v010);
face.vertices.push_back(v110);
face.vertices.push_back(v100);
}
{ // z max plane
Face& face = createFace();
face.name = "zMax";
face.plane.set(0.0,0.0,-1.0,bb.zMax());
face.vertices.push_back(v001);
face.vertices.push_back(v101);
face.vertices.push_back(v111);
face.vertices.push_back(v011);
}
}
void ConvexPolyhedron::transform(const osg::Matrix& matrix, const osg::Matrix& inverse)
{
bool requires_infinite_plane_clip = false;
ConvexPolyhedron cp = *this;
for(Faces::iterator itr = _faces.begin();
itr != _faces.end() && !requires_infinite_plane_clip;
++itr)
{
Face& face = *itr;
face.plane.transformProvidingInverse(inverse);
for(Vertices::iterator vitr = face.vertices.begin();
vitr != face.vertices.end();
++vitr)
{
osg::Vec4d v( *vitr, 1.0 );
v = v * matrix;
if( v[3] <= 0 ) {
requires_infinite_plane_clip = true;
break;
}
vitr->set( v[0]/v[3], v[1]/v[3], v[2]/v[3] );
}
}
if( requires_infinite_plane_clip ) {
*this = cp;
transformClip( matrix, inverse );
// cp.dumpGeometry(&cp._faces.back(),&cp._faces.back().plane,this);
}
// Perpective transforms and lack of precision
// occasionaly cause removal of some points
removeDuplicateVertices( );
checkCoherency( true, "ConvexPolyhedron::transform" );
}
void ConvexPolyhedron::transformClip(const osg::Matrix& matrix, const osg::Matrix& inverse)
{
double tolerance = 0.00001;
if( _faces.empty() ) return;
ConvexPolyhedron cp( *this );
typedef std::vector< FaceDistances > FaceDistancesList;
FaceDistancesList faceDistances;
faceDistances.resize( _faces.size() );
double min = FLT_MAX, max = -FLT_MAX; //Hull max & min point distances
FaceDistancesList::iterator fd = faceDistances.begin();
// First step compute each face points distances to cutting plane
for( Faces::iterator itr = _faces.begin();
itr != _faces.end();
++itr, ++fd )
{
fd->itr = itr;
fd->distances.reserve( itr->vertices.size() );
fd->on = 0;
fd->above = 0;
fd->below = 0;
itr->plane.transformProvidingInverse(inverse);
for( Vertices::iterator vitr = itr->vertices.begin();
vitr != itr->vertices.end();
++vitr)
{
osg::Vec4d p( *vitr, 1.0 );
p = p * matrix;
vitr->set( p[0]/p[3], p[1]/p[3], p[2]/p[3] );
double d = p[3]-tolerance;
fd->points.push_back( p );
fd->distances.push_back( d );
if ( d>point_plane_tolerance ) ++fd->above;
else if ( d<-point_plane_tolerance ) ++fd->below;
else ++fd->on;
min = osg::minimum( min, d );
max = osg::maximum( max, d );
}
}
if( max <= plane_hull_tolerance ) { // All points on or below cutting plane
_faces.clear();
return;
}
if( min >= -plane_hull_tolerance ) { // All points on or above cutting plane
return;
}
typedef std::pair<osg::Vec3d, osg::Vec3d> Edge;
typedef std::set< Edge > Edges;
Edges edges;
for( FaceDistancesList::iterator fd = faceDistances.begin();
fd != faceDistances.end();
++fd )
{
if ( fd->below == 0 )
{ // skip face if all points on or above cutting plane ( below == 0 )
continue;
}
if ( /* fd->below > 0 && */ fd->above == 0 && fd->on == 0 )
{
_faces.erase( fd->itr ); // remove face if points below or on
continue;
}
// cut the face if some points above and below plane
// assert( fd->below > 0 && fd->above > 0 );
Face& face = *(fd->itr);
Points& vertices = fd->points;
Distances& distances = fd->distances;
Vertices newFaceVertices;
Vertices newVertices;
for(unsigned int i=0; i < vertices.size(); ++i)
{
osg::Vec4d &va = vertices[i];
osg::Vec4d &vb = vertices[(i+1)%vertices.size()];
double &distance_a = distances[i];
double &distance_b = distances[(i+1)%vertices.size()];
// Is first edge point above or on the plane?
if ( -point_plane_tolerance <= distance_a ) {
osg::Vec3d v( vertices[i][0], vertices[i][1], vertices[i][2] );
v /= vertices[i][3];
if( newVertices.empty() || v != newVertices.back() )
newVertices.push_back( v );
if ( distance_a <= point_plane_tolerance ) {
if( newFaceVertices.empty() || v != newFaceVertices.back() )
newFaceVertices.push_back( v );
}
}
// Does edge intersect plane ?
if ( ( distance_a < -point_plane_tolerance && distance_b > point_plane_tolerance ) ||
( distance_b < -point_plane_tolerance && distance_a > point_plane_tolerance ) )
{
osg::Vec4d intersection; // Inserting vertex
double da = fabs( distance_a ), db = fabs( distance_b );
// tweaks to improve coherency of polytope after cut
if( da <= point_point_equivalence && da <= db ) {
intersection = va;
} else if( db <= point_point_equivalence && db <= da ) {
intersection = vb;
} else {
double dab4 = 0.25 * ( da + db );
if( dab4 < da && dab4 < db ) {
intersection = (vb*distance_a - va*distance_b)/(distance_a-distance_b);
} else {
osg::Vec4d v = (vb - va)/(distance_a-distance_b);
if( da < db )
intersection = va + v * distance_a;
else
intersection = vb + v * distance_b;
}
}
osg::Vec3d v( intersection[0], intersection[1], intersection[2] );
v /= intersection[3];
if( newVertices.empty() || v != newVertices.back() )
newVertices.push_back( v );
if( newFaceVertices.empty() || v != newFaceVertices.back() )
newFaceVertices.push_back( v );
}
}
if( newVertices.size() && newVertices.front() == newVertices.back() )
newVertices.pop_back();
if( newFaceVertices.size() && newFaceVertices.front() == newFaceVertices.back() )
newFaceVertices.pop_back();
if( newFaceVertices.size() == 1 ) { // This is very rare but correct
WARN
<< "ConvexPolyhedron::transformClip - Slicing face polygon returns "
<< newFaceVertices.size()
<< " points. Should be 2 (usually) or 1 (rarely)."
<< std::endl;
} else if( newFaceVertices.size() == 2 ) {
if( newFaceVertices[0] < newFaceVertices[1] ) {
edges.insert( Edge( newFaceVertices[0], newFaceVertices[1] ) );
} else {
edges.insert( Edge( newFaceVertices[1], newFaceVertices[0] ) );
}
} else if( newFaceVertices.size() > 2 ) {
#if CONVEX_POLYHEDRON_WARN_ON_INCORRECT_FACE_CUT
// This may happen if face polygon is not planar or convex.
// It may happen when face was distorted by projection transform
// or when some polygon vertices land in incorrect positions after
// some transformation by badly conditioned matrix (weak precison)
WARN
<< "ConvexPolyhedron::transformClip - Slicing face polygon returns "
<< newFaceVertices.size()
<< " points. Should be 2 (usually) or 1 (rarely)."
<< std::endl;
#endif
#if CONVEX_POLYHEDRON_DUMP_ON_INCORRECT_FACE_CUT
dumpGeometry( &face, NULL, &cp );
#endif
// Lets try to recover from this uneasy situation
// by comparing current face polygon edges cut by new plane
// with edges selected for new plane
unsigned i0 = 0, i1 = newFaceVertices.size() - 1;
unsigned j0 = 0, j1 = newVertices.size() - 1;
for( ; i0 < newFaceVertices.size(); i1 = i0++, j1 = j0++ ) {
while( newFaceVertices[i0] != newVertices[j0] ) j1 = j0++;
if( newFaceVertices[i1] == newVertices[j1] )
{
if( newFaceVertices[i0] < newFaceVertices[i1] ) {
edges.insert( Edge( newFaceVertices[i0], newFaceVertices[i1] ) );
} else {
edges.insert( Edge( newFaceVertices[i1], newFaceVertices[i0] ) );
}
}
}
}
if( newVertices.size() >= 3 ) { //Add faces with at least 3 points
#if ( CONVEX_POLYHEDRON_WARN_ON_CONCAVE_POLYGON || CONVEX_POLYHEDRON_DUMP_ON_CONCAVE_POLYGON )
int convex = isFacePolygonConvex( face );
face.vertices.swap( newVertices );
if( convex && !isFacePolygonConvex( face ) ) {
#if CONVEX_POLYHEDRON_WARN_ON_CONCAVE_POLYGON
WARN << "ConvexPolyhedron::transformClip - polygon output non convex."
<< " This may lead to other issues in ConvexPolyhedron math" << std::endl;
#endif
#if CONVEX_POLYHEDRON_DUMP_ON_CONCAVE_POLYGON
dumpGeometry( &face, NULL, &cp );
#endif
}
#else
face.vertices.swap( newVertices );
#endif
} else //Remove face reduced to 0, 1, 2 points
_faces.erase( fd->itr );
}
osg::Vec3d center( 0, 0, 0 );
unsigned count = 0;
for( Faces::iterator itr = _faces.begin();
itr != _faces.end();
++itr )
{
for( Vertices::iterator vitr = itr->vertices.begin();
vitr != itr->vertices.end();
++vitr )
{
center += *vitr;
count ++;
}
}
center /= count;
if ( edges.size() > 1 ) //Ignore faces reduced to 0, 1, 2 points
{
Face face;
face.name = "Almost infinite plane";
std::deque< osg::Vec3d > vertices;
Edges::iterator itr = edges.begin();
vertices.push_back( itr->first );
vertices.push_back( itr->second );
edges.erase( itr++ );
for( unsigned int vertices_size = 0;
vertices_size < vertices.size(); )
{
vertices_size = vertices.size();
for( itr = edges.begin(); itr != edges.end(); )
{
bool not_added = false;
if( itr->first == vertices.back() )
vertices.push_back( itr->second );
else if ( itr->first == vertices.front() )
vertices.push_front( itr->second );
else if ( itr->second == vertices.back() )
vertices.push_back( itr->first );
else if ( itr->second == vertices.front() )
vertices.push_front( itr->first );
else
not_added = true;
if( not_added )
++itr;
else
edges.erase( itr++ );
}
}
#if CONVEX_POLYHEDRON_WARN_ON_BAD_POLYGON
if( !edges.empty() ) {
WARN
<< "ConvexPolyhedron::transformClip - Building new face polygon - "
<< "Found edges not matching former polygon ends"
<< std::endl;
}
#endif
std::copy(vertices.begin(), vertices.end(), std::back_inserter(face.vertices));
face.plane.set( vertices[0], vertices[1], vertices[2] );
if( face.plane.distance( center ) < 0.0 ) face.plane.flip();
_faces.push_back(face);
// Last vertex is duplicated - remove one instance
if( face.vertices.front() == face.vertices.back() )
face.vertices.pop_back();
else {// If not duplicated then it may mean we have open polygon ;-(
#if CONVEX_POLYHEDRON_WARN_ON_BAD_POLYGON
WARN
<< "ConvexPolyhedron::transformClip - Building new face polygon - "
<< " Polygon not properly closed."
<< std::endl;
#endif
#if CONVEX_POLYHEDRON_DUMP_ON_BAD_POLYGON
dumpGeometry( &_faces.back(), NULL, &cp );
#endif
}
#if ( CONVEX_POLYHEDRON_WARN_ON_CONCAVE_POLYGON || CONVEX_POLYHEDRON_DUMP_ON_CONCAVE_POLYGON )
if( !isFacePolygonConvex( face ) ) {
#if CONVEX_POLYHEDRON_WARN_ON_CONCAVE_POLYGON
WARN << "ConvexPolyhedron::transformClip - new face polygon non convex."
<< " This may lead to other issues in ConvexPolyhedron math" << std::endl;
#endif
#if CONVEX_POLYHEDRON_DUMP_ON_CONCAVE_POLYGON
ConvexPolyhedron cp;
cp.createFace() = face;
cp.dumpGeometry( );
#endif
}
#endif
}
// Perpective transforms and lack of precision
// occasionaly cause removal of some points
removeDuplicateVertices( );
checkCoherency( true, "ConvexPolyhedron::transformClip" );
}
bool ConvexPolyhedron::mergeFaces
( const Face & face0, const Face & face1, Face & face )
{
typedef std::pair< osg::Vec3d, osg::Vec3d > Edge;
typedef std::set<Edge> Edges;
Edges edges;
for(unsigned int i=0; i<face0.vertices.size(); ++i)
{
const osg::Vec3d* va = &face0.vertices[i];
const osg::Vec3d* vb = &face0.vertices[(i+1)%face0.vertices.size()];
if( *vb == *va ) continue; // ignore duplicated point edges
if( *vb < *va ) std::swap( va, vb );
edges.insert( Edge( *va, *vb ) );
}
bool intersection = false;
for(unsigned int i=0; i<face1.vertices.size(); ++i)
{
const osg::Vec3d* va = &face1.vertices[i];
const osg::Vec3d* vb = &face1.vertices[(i+1)%face1.vertices.size()];
if( *vb == *va ) continue; // ignore duplicated point edges
if( *vb < *va ) std::swap( va, vb );
Edge edge( *va, *vb );
Edges::iterator itr = edges.find( edge );
if( itr == edges.end() )
edges.insert( edge );
else {
edges.erase( itr );
intersection = true;
}
}
if( intersection )
face.vertices.clear();
if( intersection && edges.size() > 1 ) //Ignore faces reduced to 0, 1, 2 points
{
std::deque< osg::Vec3d > vertices;
Edges::iterator itr = edges.begin();
vertices.push_back( itr->first );
vertices.push_back( itr->second );
edges.erase( itr++ );
for( unsigned int vertices_size = 0;
vertices_size < vertices.size(); )
{
vertices_size = vertices.size();
for( itr = edges.begin(); itr != edges.end(); )
{
bool not_added = false;
if( itr->first == vertices.back() )
vertices.push_back( itr->second );
else if ( itr->first == vertices.front() )
vertices.push_front( itr->second );
else if ( itr->second == vertices.back() )
vertices.push_back( itr->first );
else if ( itr->second == vertices.front() )
vertices.push_front( itr->first );
else
not_added = true;
if( not_added )
++itr;
else
edges.erase( itr++ );
}
}
#if CONVEX_POLYHEDRON_WARN_ON_BAD_POLYGON
if( !edges.empty() ) {
WARN
<< "ConvexPolyhedron::mergeFaces - Building new face polygon - "
<< "Found edges not matching former polygon ends."
<< std::endl;
}
#endif
// Last vertex is duplicated - remove one instance
if( vertices.front() == vertices.back() )
vertices.pop_back();
else {// If not duplicated then it may mean we have open polygon ;-(
#if CONVEX_POLYHEDRON_WARN_ON_BAD_POLYGON
WARN
<< "ConvexPolyhedron::mergeFaces - Building new face polygon - "
<< " Polygon not properly closed."
<< std::endl;
#endif
#if CONVEX_POLYHEDRON_DUMP_ON_BAD_POLYGON
#endif
}
#if 1 // Resulting plane will be the same as face0
face.plane = face0.plane;
std::copy(vertices.begin(), vertices.end(), std::back_inserter(face.vertices));
#else // Compute resulting plane as average of faces (not a good idea)
osg::Vec3d normal = face0.plane.getNormal() + face1.plane.getNormal();
normal.normalize();
osg::Vec3d center;
for( unsigned int i = 0; i < vertices.size(); ++i )
{
center += vertices[i];
face.vertices.push_back( vertices[i] );
}
center /= vertices.size();
face.plane.set( normal, center );
#endif
face.name = face0.name + " + " + face1.name;
// No testing for concave polys
// Its possible to build concave polygon from two merged faces
// But after all coplanar faces are merged final result should be convex.
}
return intersection;
}
void ConvexPolyhedron::mergeCoplanarFaces
( const double & dot_tolerance, const double & delta_tolerance )
{
for(Faces::iterator itr0 = _faces.begin();
itr0 != _faces.end();
++itr0 )
{
double tolerance = delta_tolerance;
for( unsigned i = 0; i < itr0->vertices.size(); ++i ) {
tolerance = osg::maximum( tolerance,
fabs( itr0->plane.distance( itr0->vertices[i] ) ) );
}
for(Faces::iterator itr1 = _faces.begin();
itr1 != _faces.end();
)
{
if( itr1 == itr0 ) {
++itr1;
continue;
}
bool attempt_merge = true;
for( unsigned i = 0; i < itr1->vertices.size(); ++i ) {
if( fabs( itr0->plane.distance( itr1->vertices[i] ) ) > tolerance )
{
attempt_merge = false;
break;
}
}
if( !attempt_merge &&
1.0 - itr0->plane.getNormal() * itr1->plane.getNormal() < dot_tolerance &&
fabs( itr0->plane.ptr()[3] - itr1->plane.ptr()[3] ) < delta_tolerance )
attempt_merge = true;
if( attempt_merge && mergeFaces( *itr0, *itr1, *itr0 ) )
itr1 = _faces.erase( itr1 );
else
++itr1;
}
}
}
void ConvexPolyhedron::removeDuplicateVertices( void )
{
#if 1
// Aggressive removal, find very close points and replace them
// with their average. Second step wil do the rest.
typedef std::map< osg::Vec3f, osg::Vec4d > Points;
typedef std::set< osg::Vec3d > VertexSet;
VertexSet vertexSet;
Points points;
for( Faces::iterator itr = _faces.begin();
itr != _faces.end();
++itr )
{
for( Vertices::iterator vitr = itr->vertices.begin();
vitr != itr->vertices.end();
++vitr )
{
vertexSet.insert( *vitr );
}
}
for( VertexSet::iterator vitr = vertexSet.begin();
vitr != vertexSet.end();
++vitr )
{
points[ *vitr ] += osg::Vec4d( *vitr, 1.0 );
}
for( Points::iterator itr = points.begin();
itr != points.end();
++itr )
{
if( itr->second[3] > 1.0 )
itr->second /= itr->second[3];
}
for( Faces::iterator itr = _faces.begin();
itr != _faces.end();
++itr )
{
for( Vertices::iterator vitr = itr->vertices.begin();
vitr != itr->vertices.end();
++vitr )
{
osg::Vec4d &v = points[ *vitr ];
*vitr = osg::Vec3d( v[0], v[1], v[2] );
}
}
#endif
for( Faces::iterator itr = _faces.begin();
itr != _faces.end();
)
{
assert( itr->vertices.size() > 0 );
osg::Vec3d prev = itr->vertices.back();
for( Vertices::iterator vitr = itr->vertices.begin();
vitr != itr->vertices.end();
)
{
if( *vitr == prev ) {
vitr = itr->vertices.erase( vitr );
} else {
prev = *vitr;
++vitr;
}
}
if( itr->vertices.size() < 3 )
itr = _faces.erase( itr );
else
++itr;
}
mergeCoplanarFaces();
#if 1
// Experimentally remove vertices on colinear edge chains
// effectivelyy replacing them with one edge
typedef std::map<osg::Vec3d,int> VertexCounter;
VertexCounter vertexCounter;
for( Faces::iterator itr = _faces.begin();
itr != _faces.end();
++itr)
{
for( Vertices::iterator vitr = itr->vertices.begin();
vitr != itr->vertices.end();
++vitr )
{
++vertexCounter[ *vitr ];
}
}
for( Faces::iterator itr = _faces.begin();
itr != _faces.end();
)
{
for( Vertices::iterator vitr = itr->vertices.begin();
vitr != itr->vertices.end();
)
{
if( vertexCounter[ *vitr ] == 2 ) {
#if 0 // Sanity check if we could remove this point without changing poly shape
Vertices::iterator next = ( vitr + 1 == itr->vertices.end() ?
itr->vertices.begin() :
vitr + 1 );
Vertices::iterator prev = ( vitr == itr->vertices.begin() ?
itr->vertices.end() - 1 :
vitr - 1 );
osg::Vec3d va = *vitr - *prev;
osg::Vec3d vb = *next - *vitr;
double da = va.normalize();
double db = vb.normalize();
if( 0.001 < da && 0.001 < db )
{
double dot = va * vb, limit = cos( 0.01 );
if( dot < limit ) {
WARN << "ConvexPolyhedron::removeDuplicateVertices"
<< " - removed mid point connecting two non colinear edges."
<< " Angle(deg): " << osg::RadiansToDegrees( acos( dot ) )
<< " Length1: " << da
<< " Length2: " << db
<< std::endl;
}
} else {
if( da == 0.0 || db == 0.0 )
WARN << "ConvexPolyhedron::removeDuplicateVertices"
<< " - removed degenerated mid point connecting two edges"
<< std::endl;
}
#endif
vitr = itr->vertices.erase( vitr );
} else {
++vitr;
}
}
if( itr->vertices.size() < 3 )
itr = _faces.erase( itr );
else
++itr;
}
#endif
checkCoherency( false, "Leave ConvexPolyhedron::removeDuplicateVertices" );
}
int ConvexPolyhedron::pointsColinear
( const osg::Vec3d & a, const osg::Vec3d & b, const osg::Vec3d & c,
const double & dot_tolerance, const double & delta_tolerance )
{
osg::Vec3d va = b - a;
osg::Vec3d vb = c - b;
double da = va.normalize();
double db = vb.normalize();
if( delta_tolerance >= da || delta_tolerance >= db )
return -1; // assume collinearity if one of the edges is zero length
if( 1.0 - fabs( va * vb ) <= dot_tolerance )
return 1; // edge normals match collinearity condition
return 0; // nope. not collinear
}
int ConvexPolyhedron::isFacePolygonConvex( Face & face, bool ignoreColinearVertices )
{
int positive = 0, negative = 0, colinear = 0;
for( unsigned int i = 0; i < face.vertices.size(); ++i )
{
osg::Vec3d va = face.vertices[i];
osg::Vec3d vb = face.vertices[(i+1)%face.vertices.size()];
osg::Vec3d vc = face.vertices[(i+2)%face.vertices.size()];
#if ( CONVEX_POLYHEDRON_WARN_ON_INCOHERENT_DATA > 1 || CONVEX_POLYHEDRON_WARN_ON_CONCAVE_POLYGON )
double dist = fabs( face.plane.distance( va ) );
if( dist > 0.0001 )
{
WARN << "ConvexPolyhedron::isFacePolygonConvex - plane point too far from plane (" << dist <<")" << std::endl;
}
#endif
// cast points on a plane
va -= face.plane.getNormal() * face.plane.distance( va );
vb -= face.plane.getNormal() * face.plane.distance( vb );
vc -= face.plane.getNormal() * face.plane.distance( vc );
if( pointsColinear( va, vb, vc ) ) {
colinear++;
} else {
double side =( ( vc - vb ) ^ ( vb - va ) ) * face.plane.getNormal();
if( side < 0 ) negative++;
if( side > 0 ) positive++;
}
}
if( !ignoreColinearVertices && colinear > 0 )
return 0;
if( !negative && !positive )
return 0;
if( (negative + colinear) == static_cast<int>(face.vertices.size()) )
return -( negative + colinear );
if( (positive + colinear) == static_cast<int>(face.vertices.size()) )
return +( positive + colinear );
return 0;
}
bool ConvexPolyhedron::checkCoherency
( bool checkForNonConvexPolys, const char * errorPrefix )
{
bool result = true;
bool convex = true;
#if CONVEX_POLYHEDRON_CHECK_COHERENCY
if( !errorPrefix ) errorPrefix = "ConvexPolyhedron";
typedef std::pair<osg::Vec3d, osg::Vec3d> Edge;
typedef std::map<Edge,int> EdgeCounter;
typedef std::map<osg::Vec3d,int> VertexCounter;
EdgeCounter edgeCounter;
VertexCounter vertexCounter;
for( Faces::iterator itr = _faces.begin();
itr != _faces.end();
++itr)
{
Face& face = *itr;
if( checkForNonConvexPolys && !isFacePolygonConvex( face ) ) {
convex = false;
#if ( 1 < CONVEX_POLYHEDRON_WARN_ON_INCOHERENT_DATA || CONVEX_POLYHEDRON_WARN_ON_CONCAVE_POLYGON )
WARN << errorPrefix <<
" - coherency fail - face polygon concave" << std::endl;
#endif
#if ( 1 < CONVEX_POLYHEDRON_DUMP_ON_INCOHERENT_DATA || CONVEX_POLYHEDRON_DUMP_ON_CONCAVE_POLYGON )
dumpGeometry( &face );
#endif
}
Vertices& vertices = face.vertices;
for(unsigned int i=0; i<vertices.size(); ++i)
{
osg::Vec3d& a = vertices[ i ];
osg::Vec3d& b = vertices[ (i+1) % vertices.size()];
++vertexCounter[ a ];
if (a<b)
++edgeCounter[Edge(a,b)];
else
++edgeCounter[Edge(b,a)];
}
}
for( EdgeCounter::iterator itr = edgeCounter.begin();
itr != edgeCounter.end();
++itr)
{
const Edge &e = itr->first;
if( e.first.isNaN() ) {
result = false;
#if ( 1 < CONVEX_POLYHEDRON_WARN_ON_INCOHERENT_DATA )
WARN << errorPrefix <<
" - coherency fail - Vertex is NaN." << std::endl;
#endif
}
if( e.second.isNaN() ) {
result = false;
#if ( 1 < CONVEX_POLYHEDRON_WARN_ON_INCOHERENT_DATA )
WARN << errorPrefix <<
" - coherency fail - Vertex is NaN." << std::endl;
#endif
}
if( e.first == e.second ) {
result = false;
#if ( 1 < CONVEX_POLYHEDRON_WARN_ON_INCOHERENT_DATA )
WARN << errorPrefix <<
" - coherency fail - Edge with identical vertices." << std::endl;
#endif
}
if( vertexCounter[ e.first ] < 3 ) {
result = false;
#if ( 1 < CONVEX_POLYHEDRON_WARN_ON_INCOHERENT_DATA )
WARN << errorPrefix <<
" - coherency fail - Vertex present " << vertexCounter[ e.first ] << " times" << std::endl;
#endif
}
if( vertexCounter[ e.second ] < 3 ) {
result = false;
#if ( 1 < CONVEX_POLYHEDRON_WARN_ON_INCOHERENT_DATA )
WARN << errorPrefix <<
" - coherency fail - Vertex present " << vertexCounter[ e.second ] << " times" << std::endl;
#endif
}
if( itr->second != 2 ) {
result = false;
#if ( 1 < CONVEX_POLYHEDRON_WARN_ON_INCOHERENT_DATA )
WARN << errorPrefix <<
" - coherency fail - Edge present " << itr->second << " times" << std::endl;
#endif
}
}
#if ( 1 == CONVEX_POLYHEDRON_WARN_ON_INCOHERENT_DATA )
if( !convex )
WARN << errorPrefix
<< " - coherency fail - non convex output" << std::endl;
if ( !result )
WARN << errorPrefix
<< " - coherency fail - incoherent output" << std::endl;
#endif
#if ( 1 == CONVEX_POLYHEDRON_DUMP_ON_INCOHERENT_DATA )
if( !result || !convex )
dumpGeometry( );
#endif
#if ( 1 < CONVEX_POLYHEDRON_DUMP_ON_INCOHERENT_DATA )
if( !result )
dumpGeometry( );
#endif
#endif // CONVEX_POLYHEDRON_CHECK_COHERENCY
return result && convex;
}
osg::BoundingBox ConvexPolyhedron::computeBoundingBox( const osg::Matrix & m ) const
{
osg::BoundingBox bb;
if( &m != &defaultMatrix ) {
for( Faces::const_iterator itr = _faces.begin(); itr != _faces.end(); ++itr )
for( Vertices::const_iterator vitr = itr->vertices.begin();
vitr != itr->vertices.end();
++vitr )
bb.expandBy( *vitr * m );
} else {
for( Faces::const_iterator itr = _faces.begin(); itr != _faces.end(); ++itr )
for( Vertices::const_iterator vitr = itr->vertices.begin();
vitr != itr->vertices.end();
++vitr )
bb.expandBy( *vitr );
}
return bb;
}
void ConvexPolyhedron::cut(const osg::Polytope & polytope)
{
const char * apc[6] = { "left", "right", "bottom", "top", "near", "far" };
char ac[16];
int i = 0;
for(osg::Polytope::PlaneList::const_iterator itr = polytope.getPlaneList().begin();
itr != polytope.getPlaneList().end();
++itr)
{
const char* arg;
if (i < 6) {
arg = apc[i];
} else {
sprintf(ac, "%d", i);
arg = ac;
}
cut(*itr, std::string( arg ) );
i++;
}
removeDuplicateVertices();
}
void ConvexPolyhedron::cut(const ConvexPolyhedron& polytope)
{
for(Faces::const_iterator itr = polytope._faces.begin();
itr != polytope._faces.end();
++itr)
{
cut(itr->plane, itr->name);
}
removeDuplicateVertices();
}
void ConvexPolyhedron::cut(const osg::Plane& plane, const std::string& name)
{
if( _faces.empty() ) return;
ConvexPolyhedron cp( *this );
typedef std::vector< FaceDistances > FaceDistancesList;
FaceDistancesList faceDistances;
faceDistances.resize( _faces.size() );
double min = FLT_MAX, max = -FLT_MAX; //Hull max & min point distances
FaceDistancesList::iterator fd = faceDistances.begin();
// First step compute each face points distances to cutting plane
for( Faces::iterator itr = _faces.begin();
itr != _faces.end();
++itr, ++fd )
{
fd->itr = itr;
fd->distances.reserve( itr->vertices.size() );
fd->on = 0;
fd->above = 0;
fd->below = 0;
#if 0 // Skip if cutting plane the same as one of faces
if( plane.ptr()[0] ) == itr->plane.ptr()[0] &&
plane.ptr()[1] ) == itr->plane.ptr()[1] &&
plane.ptr()[2] ) == itr->plane.ptr()[2] &&
#else // check plane using less precise float values
if( float( plane.ptr()[0] ) == float( itr->plane.ptr()[0] ) &&
float( plane.ptr()[1] ) == float( itr->plane.ptr()[1] ) &&
float( plane.ptr()[2] ) == float( itr->plane.ptr()[2] ) &&
#endif
plane_hull_tolerance >= fabs( float( plane.ptr()[3] )- float( itr->plane.ptr()[3] ) ) )
return;
for( Vertices::iterator vitr = itr->vertices.begin();
vitr != itr->vertices.end();
++vitr)
{
double d = plane.distance( *vitr );
fd->distances.push_back( d );
if ( d>point_plane_tolerance ) ++fd->above;
else if ( d<-point_plane_tolerance ) ++fd->below;
else ++fd->on;
min = osg::minimum( min, d );
max = osg::maximum( max, d );
}
}
if( max <= plane_hull_tolerance ) { // All points on or below cutting plane
_faces.clear();
return;
}
if( min >= -plane_hull_tolerance ) { // All points on or above cutting plane
return;
}
typedef std::pair<osg::Vec3d, osg::Vec3d> Edge;
typedef std::set< Edge > Edges;
Edges edges;
for( FaceDistancesList::iterator fd = faceDistances.begin();
fd != faceDistances.end();
++fd )
{
if ( fd->below == 0 )
{ // skip face if all points on or above cutting plane ( below == 0 )
continue;
}
if ( /* fd->below > 0 && */ fd->above == 0 && fd->on == 0 )
{
_faces.erase( fd->itr ); // remove face if points below or on
continue;
}
// cut the face if some points above and below plane
// assert( fd->below > 0 && fd->above > 0 );
Face& face = *(fd->itr);
Vertices& vertices = face.vertices;
Distances& distances = fd->distances;
Vertices newFaceVertices;
Vertices newVertices;
for(unsigned int i=0; i < vertices.size(); ++i)
{
osg::Vec3d &va = vertices[i];
osg::Vec3d &vb = vertices[(i+1)%vertices.size()];
double &distance_a = distances[i];
double &distance_b = distances[(i+1)%vertices.size()];
// Is first edge point above or on the plane?
if ( -point_plane_tolerance <= distance_a ) {
if( newVertices.empty() || vertices[i] != newVertices.back() )
newVertices.push_back( vertices[i] );
if ( distance_a <= point_plane_tolerance ) {
if( newFaceVertices.empty() || vertices[i] != newFaceVertices.back() )
newFaceVertices.push_back( vertices[i] );
}
}
// Does edge intersect plane ?
if ( ( distance_a < -point_plane_tolerance && distance_b > point_plane_tolerance ) ||
( distance_b < -point_plane_tolerance && distance_a > point_plane_tolerance ) )
{
osg::Vec3d intersection; // Inserting vertex
double da = fabs( distance_a ), db = fabs( distance_b );
// tweaks to improve coherency of polytope after cut
if( da <= point_point_equivalence && da <= db ) {
intersection = va;
} else if( db <= point_point_equivalence && db <= da ) {
intersection = vb;
} else {
double dab4 = 0.25 * ( da + db );
if( dab4 < da && dab4 < db ) {
intersection = (vb*distance_a - va*distance_b)/(distance_a-distance_b);
} else {
osg::Vec3d v = (vb - va)/(distance_a-distance_b);
if( da < db )
intersection = va + v * distance_a;
else
intersection = vb + v * distance_b;
}
}
if( newVertices.empty() || intersection != newVertices.back() )
newVertices.push_back( intersection );
if( newFaceVertices.empty() || intersection != newFaceVertices.back() )
newFaceVertices.push_back( intersection );
}
}
if( newVertices.size() && newVertices.front() == newVertices.back() )
newVertices.pop_back();
if( newFaceVertices.size() && newFaceVertices.front() == newFaceVertices.back() )
newFaceVertices.pop_back();
if( newFaceVertices.size() == 1 ) { // This is very rare but correct
WARN
<< "ConvexPolyhedron::cut - Slicing face polygon returns "
<< newFaceVertices.size()
<< " points. Should be 2 (usually) or 1 (rarely)."
<< std::endl;
} else if( newFaceVertices.size() == 2 ) {
if( newFaceVertices[0] < newFaceVertices[1] ) {
edges.insert( Edge( newFaceVertices[0], newFaceVertices[1] ) );
} else {
edges.insert( Edge( newFaceVertices[1], newFaceVertices[0] ) );
}
} else if( newFaceVertices.size() > 2 ) {
#if CONVEX_POLYHEDRON_WARN_ON_INCORRECT_FACE_CUT
// This may happen if face polygon is not planar or convex.
// It may happen when face was distorted by projection transform
// or when some polygon vertices land in incorrect positions after
// some transformation by badly conditioned matrix (weak precison)
WARN
<< "ConvexPolyhedron::cut - Slicing face polygon returns "
<< newFaceVertices.size()
<< " points. Should be 2 (usually) or 1 (rarely)."
<< std::endl;
#endif
#if CONVEX_POLYHEDRON_DUMP_ON_INCORRECT_FACE_CUT
dumpGeometry( &face, &plane );
#endif
// Let try to recover from this uneasy situation
// by comparing current face polygon edges cut by new plane
// with edges selected for new plane
unsigned i0 = 0, i1 = newFaceVertices.size() - 1;
unsigned j0 = 0, j1 = newVertices.size() - 1;
for( ; i0 < newFaceVertices.size(); i1 = i0++, j1 = j0++ ) {
while( newFaceVertices[i0] != newVertices[j0] ) j1 = j0++;
if( newFaceVertices[i1] == newVertices[j1] )
{
if( newFaceVertices[i0] < newFaceVertices[i1] ) {
edges.insert( Edge( newFaceVertices[i0], newFaceVertices[i1] ) );
} else {
edges.insert( Edge( newFaceVertices[i1], newFaceVertices[i0] ) );
}
}
}
}
if( newVertices.size() >= 3 ) { //Add faces with at least 3 points
#if ( CONVEX_POLYHEDRON_WARN_ON_CONCAVE_POLYGON || CONVEX_POLYHEDRON_DUMP_ON_CONCAVE_POLYGON )
int convex = isFacePolygonConvex( face );
vertices.swap( newVertices );
if( convex && !isFacePolygonConvex( face ) ) {
#if CONVEX_POLYHEDRON_WARN_ON_CONCAVE_POLYGON
WARN << "ConvexPolyhedron::cut - polygon output non convex."
<< " This may lead to other issues in ConvexPolyhedron math" << std::endl;
#endif
#if CONVEX_POLYHEDRON_DUMP_ON_CONCAVE_POLYGON
dumpGeometry( &face, &plane, &cp );
#endif
}
#else
vertices.swap( newVertices );
#endif
} else //Remove face reduced to 0, 1, 2 points
_faces.erase( fd->itr );
}
if ( edges.size() > 1 ) //Ignore faces reduced to 0, 1, 2 points
{
Face face;
face.name = name;
face.plane = plane;
std::deque< osg::Vec3d > vertices;
Edges::iterator itr = edges.begin();
vertices.push_back( itr->first );
vertices.push_back( itr->second );
edges.erase( itr++ );
for( unsigned int vertices_size = 0;
vertices_size < vertices.size(); )
{
vertices_size = vertices.size();
for( itr = edges.begin(); itr != edges.end(); )
{
bool not_added = false;
if( itr->first == vertices.back() )
vertices.push_back( itr->second );
else if ( itr->first == vertices.front() )
vertices.push_front( itr->second );
else if ( itr->second == vertices.back() )
vertices.push_back( itr->first );
else if ( itr->second == vertices.front() )
vertices.push_front( itr->first );
else
not_added = true;
if( not_added )
++itr;
else
edges.erase( itr++ );
}
}
#if CONVEX_POLYHEDRON_WARN_ON_BAD_POLYGON
if( !edges.empty() ) {
WARN
<< "ConvexPolyhedron::cut - Building new face polygon - "
<< "Found edges not matching former polygon ends"
<< std::endl;
}
#endif
std::copy(vertices.begin(), vertices.end(), std::back_inserter(face.vertices));
_faces.push_back(face);
// Last vertex is duplicated - remove one instance
if( face.vertices.front() == face.vertices.back() )
face.vertices.pop_back();
else {// If not duplicated then it may mean we have open polygon ;-(
#if CONVEX_POLYHEDRON_WARN_ON_BAD_POLYGON
WARN
<< "ConvexPolyhedron::cut - Building new face polygon - "
<< " Polygon not properly closed."
<< std::endl;
#endif
#if CONVEX_POLYHEDRON_DUMP_ON_BAD_POLYGON
dumpGeometry( &_faces.back(), &plane, &cp );
#endif
}
#if ( CONVEX_POLYHEDRON_WARN_ON_CONCAVE_POLYGON || CONVEX_POLYHEDRON_DUMP_ON_CONCAVE_POLYGON )
if( !isFacePolygonConvex( face ) ) {
#if CONVEX_POLYHEDRON_DUMP_ON_CONCAVE_POLYGON
ConvexPolyhedron cp;
cp.createFace() = face;
cp.dumpGeometry( );
#endif
#if CONVEX_POLYHEDRON_WARN_ON_CONCAVE_POLYGON
WARN << "ConvexPolyhedron::cut - new face polygon non convex."
<< " This may lead to other issues in ConvexPolyhedron math" << std::endl;
#endif
}
#endif
}
// removeDuplicateVertices( );
}
////////////////////////////////////////////////////////////////////////////
void ConvexPolyhedron::extrude( const osg::Vec3d & offset )
{
if( offset.length2() == 0 ) return;
typedef std::pair<osg::Vec3d, osg::Vec3d> Edge;
typedef std::vector<Face*> EdgeFaces;
typedef std::map<Edge, EdgeFaces> EdgeMap;
EdgeMap edgeMap;
// Build edge maps
for(Faces::iterator itr = _faces.begin();
itr != _faces.end();
++itr)
{
Face& face = *itr;
for(unsigned int i=0; i<face.vertices.size(); ++i)
{
osg::Vec3d& va = face.vertices[i];
osg::Vec3d& vb = face.vertices[(i+1)%face.vertices.size()];
if (va < vb) edgeMap[Edge(va,vb)].push_back(&face);
else edgeMap[Edge(vb,va)].push_back(&face);
}
}
// Offset faces
for(Faces::iterator itr = _faces.begin();
itr != _faces.end();
++itr)
{
Face& face = *itr;
double dotOffset = face.plane.dotProductNormal( offset );
if( dotOffset >= 0 ) continue;
face.plane.ptr()[3] -= dotOffset;
for(unsigned int i=0; i<face.vertices.size(); ++i)
{
face.vertices[i] += offset;
}
}
typedef std::set< Edge > SilhouetteEdges;
typedef std::map< Face*, SilhouetteEdges > SilhouetteFaces;
SilhouetteFaces silhouetteFaces;
int new_face_counter = 0;
// Now add new faces from slhouette edges extrusion
for(EdgeMap::iterator eitr = edgeMap.begin();
eitr != edgeMap.end();
++eitr)
{
const Edge& edge = eitr->first;
const EdgeFaces& edgeFaces = eitr->second;
if ( edgeFaces.size()==1 )
{
// WL: Execution should not reach this line.
// If you got here let me know: lewandowski@ai.com.pl
assert( 0 );
}
else if ( edgeFaces.size()==2 )
{
#if 0 // Use float normal computations
osg::Vec3f vf( offset );
double dotOffset0 = osg::Vec3f( edgeFaces[0]->plane.getNormal() ) * vf;
double dotOffset1 = osg::Vec3f( edgeFaces[1]->plane.getNormal() ) * vf;
#else
double dotOffset0 = edgeFaces[0]->plane.getNormal() * offset;
double dotOffset1 = edgeFaces[1]->plane.getNormal() * offset;
#endif
//Select orthogonal faces and vertices appropriate for offsetting
if( (dotOffset0 == 0.0 && dotOffset1 < 0.0) ||
(dotOffset1 == 0.0 && dotOffset0 < 0.0) )
{
Face * face = ( dotOffset0 == 0 ? edgeFaces[0] : edgeFaces[1] );
silhouetteFaces[ face ].insert( edge );
}
if( (dotOffset0 < 0.0 && dotOffset1 > 0.0) ||
(dotOffset1 < 0.0 && dotOffset0 > 0.0) )
{
Face & face = createFace();
char ac[40] = "Side plane from edge extrude ";
sprintf(ac + strlen(ac), "%d", new_face_counter++);
face.name = ac;
// Compute face plane
face.vertices.push_back( edge.first );
face.vertices.push_back( edge.second );
osg::Vec3d n = face.vertices[0] - face.vertices[1];
n.normalize();
n = ( n ^ offset );
n.normalize();
if( n * ( edgeFaces[1]->plane.getNormal() + edgeFaces[0]->plane.getNormal() ) < 0 )
{
n = -n;
std::swap( face.vertices[1], face.vertices[0] );
}
face.vertices.push_back( face.vertices[1] + offset );
face.vertices.push_back( face.vertices[0] + offset );
face.plane.set( n,(face.vertices[0] + face.vertices[1] +
face.vertices[2] + face.vertices[3]) * .25 );
}
}
else if( edgeFaces.size() > 2 )
{
assert( 0 );
// WL: Execution should not reach this line.
// If you got here let me know: lewandowski@ai.com.pl
}
}
// Finally update faces which are orthogonal to our normal
for(SilhouetteFaces::iterator itr = silhouetteFaces.begin();
itr != silhouetteFaces.end();
++itr)
{
SilhouetteEdges & edges = itr->second;
Vertices & vertices = itr->first->vertices;
Vertices newVertices;
for( unsigned int i = 0; i < vertices.size(); i++ )
{
osg::Vec3d
&va = vertices[ ( i + vertices.size() - 1 ) % vertices.size() ],
&vb = vertices[ i ],
&vc = vertices[ ( i + 1 ) % vertices.size() ];
Edge eab = va < vb ? Edge( va, vb ) : Edge( vb, va );
Edge ebc = vb < vc ? Edge( vb, vc ) : Edge( vc, vb );
bool abFound = edges.find( eab ) != edges.end();
bool bcFound = edges.find( ebc ) != edges.end();
if( abFound && bcFound ) {
newVertices.push_back( vb + offset );
} else if( !abFound && !bcFound ) {
newVertices.push_back( vb );
} else if( !abFound && bcFound ) {
newVertices.push_back( vb );
newVertices.push_back( vb + offset );
} else if( abFound && !bcFound ) {
newVertices.push_back( vb + offset );
newVertices.push_back( vb );
}
}
vertices.swap( newVertices );
}
removeDuplicateVertices( );
checkCoherency( true, "ConvexPolyhedron::extrude" );
}
////////////////////////////////////////////////////////////////////////////
void ConvexPolyhedron::translate( const osg::Vec3d & offset )
{
for( Faces::iterator itr = _faces.begin(); itr != _faces.end(); ++itr )
{
itr->plane.ptr()[3] -= itr->plane.dotProductNormal( offset );
for( Vertices::iterator vitr = itr->vertices.begin();
vitr != itr->vertices.end();
++vitr )
{
*vitr += offset;
}
}
}
////////////////////////////////////////////////////////////////////////////
void ConvexPolyhedron::getPolytope(osg::Polytope& polytope) const
{
for(Faces::const_iterator itr = _faces.begin();
itr != _faces.end();
++itr)
{
polytope.add(itr->plane);
}
}
////////////////////////////////////////////////////////////////////////////
void ConvexPolyhedron::getPoints(Vertices& vertices) const
{
typedef std::set<osg::Vec3d> VerticesSet;
VerticesSet verticesSet;
for(Faces::const_iterator itr = _faces.begin();
itr != _faces.end();
++itr)
{
const Face& face = *itr;
for(Vertices::const_iterator vitr = face.vertices.begin();
vitr != face.vertices.end();
++vitr)
{
verticesSet.insert(*vitr);
}
}
for(VerticesSet::iterator sitr = verticesSet.begin();
sitr != verticesSet.end();
++sitr)
{
vertices.push_back(*sitr);
}
}
////////////////////////////////////////////////////////////////////////////
osg::Geometry* ConvexPolyhedron::buildGeometry( const osg::Vec4d& colorOutline,
const osg::Vec4d& colorInside,
osg::Geometry* geometry ) const
{
if( !geometry ) {
geometry = new osg::Geometry;
} else {
geometry->getPrimitiveSetList( ).clear();
}
osg::Vec3dArray* vertices = new osg::Vec3dArray;
geometry->setVertexArray(vertices);
osg::Vec4Array* colors = new osg::Vec4Array;
geometry->setColorArray(colors, osg::Array::BIND_PER_PRIMITIVE_SET);
for(Faces::const_iterator itr = _faces.begin();
itr != _faces.end();
++itr)
{
if( colorInside[3] > 0 ) {
geometry->addPrimitiveSet( new osg::DrawArrays( GL_TRIANGLE_FAN,
vertices->size(), itr->vertices.size() ) );
colors->push_back( colorInside );
}
if( colorOutline[3] > 0 ) {
geometry->addPrimitiveSet( new osg::DrawArrays( GL_LINE_LOOP,
vertices->size(), itr->vertices.size() ) );
colors->push_back( colorOutline );
}
vertices->insert
( vertices->end(), itr->vertices.begin(), itr->vertices.end() );
}
osg::StateSet* stateset = geometry->getOrCreateStateSet();
stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
stateset->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF);
stateset->setTextureMode(1, GL_TEXTURE_2D, osg::StateAttribute::OFF);
return geometry;
}
////////////////////////////////////////////////////////////////////////////
bool ConvexPolyhedron::dumpGeometry
( const Face * face,
const osg::Plane * plane,
ConvexPolyhedron * base,
const char * filename,
const osg::Vec4d& colorOutline,
const osg::Vec4d& colorInside,
const osg::Vec4d& faceColorOutline,
const osg::Vec4d& faceColorInside,
const osg::Vec4d& planeColorOutline,
const osg::Vec4d& planeColorInside,
const osg::Vec4d& baseColorOutline,
const osg::Vec4d& baseColorInside ) const
{
osg::Group * group = new osg::Group();
osg::Geode * geode = new osg::Geode();
geode->getOrCreateStateSet()->setMode( GL_BLEND, osg::StateAttribute::ON );
geode->getOrCreateStateSet()->setMode( GL_CULL_FACE, osg::StateAttribute::OFF );
geode->getOrCreateStateSet()->setMode( GL_DEPTH_TEST, osg::StateAttribute::OFF );
group->addChild( geode );
Vertices vertices;
getPoints( vertices );
osg::BoundingBox bb;
for( unsigned int i = 0; i < vertices.size(); i++ )
bb.expandBy( vertices[i] );
ConvexPolyhedron cp( *this ), cpFace;
for( Faces::iterator itr = cp._faces.begin(); itr != cp._faces.end(); )
{
bool found = ( face &&
itr->name == face->name &&
itr->plane == face->plane &&
itr->vertices == face->vertices );
#if 1
if( cp.isFacePolygonConvex( *itr ) < 0 )
std::reverse( itr->vertices.begin(), itr->vertices.end() );
#endif
if( found ) {
cpFace.createFace() = *face;
itr = cp._faces.erase( itr );
} else {
++itr;
}
}
osg::Geometry * geometry = cp.buildGeometry( colorOutline, colorInside );
geometry->getOrCreateStateSet()->setMode( GL_CULL_FACE, osg::StateAttribute::ON );
geode->addDrawable( geometry );
if( face )
geode->addDrawable( cpFace.buildGeometry( faceColorOutline, faceColorInside ) );
if( plane )
{
ConvexPolyhedron cp;
Face & face = cp.createFace();
face.plane = *plane;
osg::Vec3d normal = face.plane.getNormal();
osg::Vec3d side = fabs(normal.x()) < fabs(normal.y()) ?
osg::Vec3d(1.0, 0.0, 0.0) :
osg::Vec3d(0.0, 1.0, 0.0);
osg::Vec3d v = normal ^ side;
v.normalize();
v *= bb.radius();
osg::Vec3d u = v ^ normal;
u.normalize();
u *= bb.radius();
osg::Vec3d c = bb.center();
c -= face.plane.getNormal() * face.plane.distance( c );
face.vertices.push_back( c - u - v );
face.vertices.push_back( c - u + v );
face.vertices.push_back( c + u + v );
face.vertices.push_back( c + u - v );
geode->addDrawable( cp.buildGeometry( planeColorOutline, planeColorInside ) );
}
if( base )
geode->addDrawable( base->buildGeometry( baseColorOutline, baseColorInside ) );
return osgDB::writeNodeFile( *group, std::string( filename ) );
}
////////////////////////////////////////////////////////////////////////////