858 lines
30 KiB
C++
858 lines
30 KiB
C++
/**************************************************************************************/
|
|
/* */
|
|
/* Visualization Library */
|
|
/* http://visualizationlibrary.org */
|
|
/* */
|
|
/* Copyright (c) 2005-2020, Michele Bosi */
|
|
/* All rights reserved. */
|
|
/* */
|
|
/* Redistribution and use in source and binary forms, with or without modification, */
|
|
/* are permitted provided that the following conditions are met: */
|
|
/* */
|
|
/* - Redistributions of source code must retain the above copyright notice, this */
|
|
/* list of conditions and the following disclaimer. */
|
|
/* */
|
|
/* - Redistributions in binary form must reproduce the above copyright notice, this */
|
|
/* list of conditions and the following disclaimer in the documentation and/or */
|
|
/* other materials provided with the distribution. */
|
|
/* */
|
|
/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND */
|
|
/* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED */
|
|
/* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE */
|
|
/* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR */
|
|
/* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES */
|
|
/* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; */
|
|
/* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON */
|
|
/* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */
|
|
/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
|
|
/* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
|
|
/* */
|
|
/**************************************************************************************/
|
|
|
|
#ifndef PolygonSimplifier_INCLUDE_ONCE
|
|
#define PolygonSimplifier_INCLUDE_ONCE
|
|
|
|
#include <vlGraphics/link_config.hpp>
|
|
#include <vlCore/Object.hpp>
|
|
#include <vlCore/Vector3.hpp>
|
|
#include <vlCore/glsl_math.hpp>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
|
|
namespace vl
|
|
{
|
|
class Geometry;
|
|
//-----------------------------------------------------------------------------
|
|
// PolygonSimplifier
|
|
//-----------------------------------------------------------------------------
|
|
/**
|
|
* The PolygonSimplifier class reduces the amount of polygons present in a Geometry using a quadric error metric.
|
|
* The algorithm simplifies only the position array of the Geometry all the other vertex attributes will be discarded.
|
|
*/
|
|
class VLGRAPHICS_EXPORT PolygonSimplifier: public Object
|
|
{
|
|
VL_INSTRUMENT_CLASS(vl::PolygonSimplifier, Object)
|
|
|
|
public:
|
|
class Vertex;
|
|
//-----------------------------------------------------------------------------
|
|
// QErr
|
|
//-----------------------------------------------------------------------------
|
|
//! The quadric error metric as defined by PolygonSimplifier.
|
|
class QErr
|
|
{
|
|
public:
|
|
QErr()
|
|
{
|
|
a2 = 0.0;
|
|
ab = 0.0;
|
|
ac = 0.0;
|
|
ad = 0.0;
|
|
b2 = 0.0;
|
|
bc = 0.0;
|
|
bd = 0.0;
|
|
c2 = 0.0;
|
|
cd = 0.0;
|
|
d2 = 0.0;
|
|
}
|
|
|
|
QErr(const dvec3& n, double d, double w = 1.0)
|
|
{
|
|
a2 = w * n.x() * n.x();
|
|
ab = w * n.x() * n.y();
|
|
ac = w * n.x() * n.z();
|
|
ad = w * n.x() * d;
|
|
b2 = w * n.y() * n.y();
|
|
bc = w * n.y() * n.z();
|
|
bd = w * n.y() * d;
|
|
c2 = w * n.z() * n.z();
|
|
cd = w * n.z() * d;
|
|
d2 = w * d*d;
|
|
|
|
// indeterminate cases
|
|
VL_CHECK( d2 == d2 )
|
|
}
|
|
|
|
dmat3 matrix() const
|
|
{
|
|
return dmat3(
|
|
a2, ab, ac,
|
|
ab, b2, bc,
|
|
ac, bc, c2 );
|
|
}
|
|
|
|
dvec3 vector() const
|
|
{
|
|
return dvec3( ad, bd, cd );
|
|
}
|
|
|
|
double offset() const
|
|
{
|
|
return d2;
|
|
}
|
|
|
|
double evaluate(const dvec3& v) const
|
|
{
|
|
return v.x()*v.x()*a2 + 2*v.x()*v.y()*ab + 2*v.x()*v.z()*ac + 2*v.x()*ad
|
|
+ v.y()*v.y()*b2 + 2*v.y()*v.z()*bc + 2*v.y()*bd
|
|
+ v.z()*v.z()*c2 + 2*v.z()*cd
|
|
+ d2;
|
|
}
|
|
|
|
bool analyticSolution(dvec3& v) const
|
|
{
|
|
#if 0
|
|
dmat3 Ainv;
|
|
double det = matrix().getInverse(Ainv);
|
|
if (!det)
|
|
return false;
|
|
v = -(Ainv*vector());
|
|
return true;
|
|
#else
|
|
double A = c2*b2-bc*bc;
|
|
double B = bc*ac-c2*ab;
|
|
double C = bc*ab-b2*ac;
|
|
double det = a2*(A)+ab*(B)+ac*(C);
|
|
if (fabs(det) < 0.0000001)
|
|
return false;
|
|
else
|
|
{
|
|
double inv_det = 1.0 / det;
|
|
dmat3 Ainv( A*inv_det, B*inv_det, C*inv_det,
|
|
(ac*bc-c2*ab)*inv_det, (c2*a2-ac*ac)*inv_det, (ab*ac-bc*a2)*inv_det,
|
|
(bc*ab-ac*b2)*inv_det, (ac*ab-bc*a2)*inv_det, (b2*a2-ab*ab)*inv_det );
|
|
v = Ainv * dvec3( -ad, -bd, -cd );
|
|
return true;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
QErr operator+(const QErr& other)
|
|
{
|
|
QErr q = *this;
|
|
q.a2 += other.a2;
|
|
q.ab += other.ab;
|
|
q.ac += other.ac;
|
|
q.ad += other.ad;
|
|
q.b2 += other.b2;
|
|
q.bc += other.bc;
|
|
q.bd += other.bd;
|
|
q.c2 += other.c2;
|
|
q.cd += other.cd;
|
|
q.d2 += other.d2;
|
|
return q;
|
|
}
|
|
|
|
const QErr& operator+=(const QErr& other)
|
|
{
|
|
a2 += other.a2;
|
|
ab += other.ab;
|
|
ac += other.ac;
|
|
ad += other.ad;
|
|
b2 += other.b2;
|
|
bc += other.bc;
|
|
bd += other.bd;
|
|
c2 += other.c2;
|
|
cd += other.cd;
|
|
d2 += other.d2;
|
|
return *this;
|
|
}
|
|
|
|
protected:
|
|
// coefficients
|
|
double a2, ab, ac, ad;
|
|
double b2, bc, bd;
|
|
double c2, cd;
|
|
double d2;
|
|
};
|
|
//-----------------------------------------------------------------------------
|
|
// Triangle
|
|
//-----------------------------------------------------------------------------
|
|
//! A Triangle as defined by PolygonSimplifier.
|
|
class Triangle
|
|
{
|
|
friend class PolygonSimplifier;
|
|
friend class Vertex;
|
|
public:
|
|
Triangle(): mRemoved(false)
|
|
{
|
|
mVertices[0] = NULL;
|
|
mVertices[1] = NULL;
|
|
mVertices[2] = NULL;
|
|
}
|
|
|
|
inline void replaceVertex( Vertex* oldv, Vertex* newv );
|
|
inline void computeNormal();
|
|
inline float computeArea() const;
|
|
inline float computePotentialArea(const Vertex* oldv, const Vertex* newv) const;
|
|
inline fvec3 computePotentialNormal(const Vertex* oldv, const Vertex* newv) const;
|
|
inline bool hasVertex(const Vertex*v) const;
|
|
inline bool checkTriangle() const;
|
|
// inline float computeDistance(const fvec3&) const;
|
|
inline QErr computeQErr() const;
|
|
|
|
//! vertices of the triangle
|
|
const Vertex* vertex(int index) const { return mVertices[index]; }
|
|
Vertex* vertex(int index) { return mVertices[index]; }
|
|
//! normal of the triangle
|
|
const fvec3& normal() const { return mNormal; }
|
|
//! ara of the triangle
|
|
// float area() const { return mArea; }
|
|
//! has this triangle been removed?
|
|
bool removed() const { return mRemoved; }
|
|
//! generates the QErr
|
|
|
|
protected:
|
|
//! vertices of the triangle
|
|
Vertex* mVertices[3];
|
|
//! normal of the triangle
|
|
fvec3 mNormal;
|
|
//! ara of the triangle
|
|
// float mArea;
|
|
//! has this triangle been removed?
|
|
bool mRemoved;
|
|
};
|
|
//-----------------------------------------------------------------------------
|
|
// Vertex
|
|
//-----------------------------------------------------------------------------
|
|
//! A Vertex as defined by PolygonSimplifier.
|
|
class Vertex
|
|
{
|
|
friend class Triangle;
|
|
friend class PolygonSimplifier;
|
|
public:
|
|
Vertex(): mCollapseCost(0.0f), mOriginalIndex(-1) , mSimplifiedIndex(-1), mRemoveOrder(-1),
|
|
mRemoved(false), mProtected(false), mAlreadyProcessed(false)
|
|
{
|
|
}
|
|
|
|
inline void addAdjacentVertex(Vertex* v);
|
|
inline void removeAdjacentVertex(Vertex* v);
|
|
inline void computeAdjacentVertices();
|
|
inline bool checkConnectivity();
|
|
inline bool isAdjacentVertex(Vertex*) const;
|
|
inline bool isIncidentTriangle(Triangle*) const;
|
|
inline void discardRemovedTriangles();
|
|
inline void removeIncidentTriangle(const Triangle*);
|
|
inline bool checkTriangles() const;
|
|
inline void computeEdgePenalty();
|
|
|
|
//! the position
|
|
const fvec3& position() const { return mPosition; }
|
|
//! ajacent vertices
|
|
int adjacentVerticesCount() const { return (int)mAdjacentVerts.size(); }
|
|
Vertex* adjacentVertex(int index) const { return mAdjacentVerts[index]; }
|
|
//! adjacent triangles
|
|
int incidentTrianglesCount() const { return (int)mIncidentTriangles.size(); }
|
|
Triangle* incidentTriangle(int index) const { return mIncidentTriangles[index]; }
|
|
//! vertex to which collapse
|
|
Vertex* collapseVertex() const { return mCollapseVertex; }
|
|
//! cost of the collapse
|
|
float collapseCost() const { return mCollapseCost; }
|
|
//! collapse position
|
|
const fvec3& collapsePosition() const { return mCollapsePosition; }
|
|
void setCollapsePosition(const fvec3& pos) { mCollapsePosition = pos; }
|
|
//! when the vertex has collapsed
|
|
int removeOrder() const { return mRemoveOrder; }
|
|
//! has the vertex been removed
|
|
bool removed() const { return mRemoved; }
|
|
//! is the vertex protected?
|
|
bool isProtected() const { return mProtected; }
|
|
//! original index of this vertex
|
|
int originalIndex() const { return mOriginalIndex; }
|
|
//! Internally used to regenerated the index buffer
|
|
int simplifiedIndex() const { return mSimplifiedIndex; }
|
|
//! Internally used
|
|
bool alreadyProcessed() const { return mAlreadyProcessed; }
|
|
//! Accumulated vertex error
|
|
const QErr& qerr() const { return mQErr; }
|
|
void setQErr(const QErr& qerr) { mQErr = qerr; }
|
|
void addQErr(const QErr& qerr) { mQErr += qerr; }
|
|
|
|
protected:
|
|
QErr mQErr;
|
|
//! the position
|
|
fvec3 mPosition;
|
|
//! ajacent vertices
|
|
std::vector< Vertex* > mAdjacentVerts;
|
|
//! adjacent triangles
|
|
std::vector< Triangle* > mIncidentTriangles;
|
|
//! vertex to which collapse
|
|
Vertex* mCollapseVertex;
|
|
//! cost of the collapse
|
|
float mCollapseCost;
|
|
//! the collapse position
|
|
fvec3 mCollapsePosition;
|
|
//! original index of this vertex
|
|
int mOriginalIndex;
|
|
//! only used during index buffer regeneration
|
|
int mSimplifiedIndex;
|
|
//! when the vertex has collapsed
|
|
int mRemoveOrder;
|
|
//! has the vertex been removed
|
|
bool mRemoved;
|
|
//! is the vertex protected?
|
|
bool mProtected;
|
|
//! internally used
|
|
bool mAlreadyProcessed;
|
|
};
|
|
|
|
public:
|
|
PolygonSimplifier(): mRemoveDoubles(false), mVerbose(true), mQuick(true) {}
|
|
|
|
void simplify();
|
|
void simplify(const std::vector<fvec3>& in_verts, const std::vector<int>& in_tris);
|
|
|
|
void setIntput(Geometry* geom) { mInput = geom; }
|
|
Geometry* input() { return mInput.get(); }
|
|
const Geometry* input() const { return mInput.get(); }
|
|
|
|
std::vector< u32 >& targets() { return mTargets; }
|
|
const std::vector< u32 >& targets() const { return mTargets; }
|
|
|
|
std::vector< ref<Geometry> >& output() { return mOutput; }
|
|
const std::vector< ref<Geometry> >& output() const { return mOutput; }
|
|
|
|
void setProtectedVertices(const std::vector<int>& protected_verts) { mProtectedVerts = protected_verts; }
|
|
|
|
int simplifiedVerticesCount() const { return (int)mSimplifiedVertices.size(); }
|
|
Vertex* simplifiedVertices(int index) const { return mSimplifiedVertices[index]; }
|
|
|
|
int simplifiedTrianglesCount() const { return (int)mSimplifiedTriangles.size(); }
|
|
Triangle* simplifiedTriangles(int index) const { return mSimplifiedTriangles[index]; }
|
|
|
|
void clearTrianglesAndVertices();
|
|
|
|
bool removeDoubles() const { return mRemoveDoubles; }
|
|
void setRemoveDoubles(bool remove_doubles) { mRemoveDoubles = remove_doubles; }
|
|
|
|
bool verbose() const { return mVerbose; }
|
|
void setVerbose(bool verbose) { mVerbose = verbose; }
|
|
|
|
bool quick() const { return mQuick; }
|
|
void setQuick(bool quick) { mQuick = quick; }
|
|
|
|
protected:
|
|
void outputSimplifiedGeometry();
|
|
inline void collapse(Vertex* v);
|
|
inline void computeCollapseInfo(Vertex* v);
|
|
|
|
protected:
|
|
ref<Geometry> mInput;
|
|
std::vector< ref<Geometry> > mOutput;
|
|
std::vector< u32 > mTargets;
|
|
std::vector<Vertex*> mSimplifiedVertices;
|
|
std::vector<Triangle*> mSimplifiedTriangles;
|
|
std::vector<int> mProtectedVerts;
|
|
bool mRemoveDoubles;
|
|
bool mVerbose;
|
|
bool mQuick;
|
|
|
|
private:
|
|
std::vector<Triangle> mTriangleLump;
|
|
std::vector<Vertex> mVertexLump;
|
|
};
|
|
//-----------------------------------------------------------------------------
|
|
// Vertex
|
|
//-----------------------------------------------------------------------------
|
|
inline void PolygonSimplifier::Vertex::addAdjacentVertex(Vertex* v)
|
|
{
|
|
if( v != this )
|
|
{
|
|
for(int i=0; i<adjacentVerticesCount(); ++i)
|
|
if (mAdjacentVerts[i] == v)
|
|
return;
|
|
mAdjacentVerts.push_back(v);
|
|
}
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
inline void PolygonSimplifier::Vertex::removeAdjacentVertex(Vertex* v)
|
|
{
|
|
VL_CHECK( v != this )
|
|
VL_CHECK( std::find(mAdjacentVerts.begin(), mAdjacentVerts.end(), v) != mAdjacentVerts.end() )
|
|
for(int i=0; i<adjacentVerticesCount(); ++i)
|
|
if (mAdjacentVerts[i] == v)
|
|
{
|
|
mAdjacentVerts.erase(mAdjacentVerts.begin() + i);
|
|
return;
|
|
}
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
inline void PolygonSimplifier::Vertex::computeAdjacentVertices()
|
|
{
|
|
mAdjacentVerts.clear();
|
|
for(int itri=0; itri<incidentTrianglesCount(); ++itri)
|
|
{
|
|
VL_CHECK(!mIncidentTriangles[itri]->mRemoved)
|
|
addAdjacentVertex( mIncidentTriangles[itri]->mVertices[0] );
|
|
addAdjacentVertex( mIncidentTriangles[itri]->mVertices[1] );
|
|
addAdjacentVertex( mIncidentTriangles[itri]->mVertices[2] );
|
|
}
|
|
mRemoved = mAdjacentVerts.empty();
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
inline bool PolygonSimplifier::Vertex::checkTriangles() const
|
|
{
|
|
for(int itri=incidentTrianglesCount(); itri--; )
|
|
if ( !incidentTriangle(itri)->checkTriangle() )
|
|
return false;
|
|
return true;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
inline void PolygonSimplifier::Vertex::computeEdgePenalty()
|
|
{
|
|
for(int ivert=0; ivert<adjacentVerticesCount(); ++ivert)
|
|
{
|
|
int edge_count = 0;
|
|
int border_tri = -1;
|
|
for(int itri=0; itri<incidentTrianglesCount() && edge_count<=1; ++itri)
|
|
{
|
|
if ( incidentTriangle(itri)->hasVertex( adjacentVertex(ivert) ) )
|
|
{
|
|
border_tri = itri;
|
|
++edge_count;
|
|
}
|
|
}
|
|
if ( edge_count == 1 )
|
|
{
|
|
fvec3 edge = position() - adjacentVertex(ivert)->position();
|
|
dvec3 n = (dvec3)cross(incidentTriangle(border_tri)->normal(), edge );
|
|
n.normalize();
|
|
double d = -dot(n,(dvec3)position());
|
|
mQErr += QErr( n, d, dot(edge, edge) * 1.0 );
|
|
}
|
|
}
|
|
}
|
|
inline void PolygonSimplifier::Vertex::removeIncidentTriangle(const Triangle* tri)
|
|
{
|
|
for(int itri=incidentTrianglesCount(); itri--; )
|
|
{
|
|
if (mIncidentTriangles[itri] == tri)
|
|
{
|
|
mIncidentTriangles.erase( mIncidentTriangles.begin() + itri );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
inline void PolygonSimplifier::Vertex::discardRemovedTriangles()
|
|
{
|
|
for(int itri=incidentTrianglesCount(); itri--; )
|
|
{
|
|
if (mIncidentTriangles[itri]->mRemoved)
|
|
mIncidentTriangles.erase( mIncidentTriangles.begin() + itri );
|
|
}
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
inline bool PolygonSimplifier::Vertex::isAdjacentVertex(Vertex* v) const
|
|
{
|
|
for(int i=0; i<adjacentVerticesCount(); ++i)
|
|
if ( adjacentVertex(i) == v )
|
|
return true;
|
|
return false;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
inline bool PolygonSimplifier::Vertex::isIncidentTriangle(Triangle* t) const
|
|
{
|
|
for(int i=0; i<incidentTrianglesCount(); ++i)
|
|
if ( incidentTriangle(i) == t )
|
|
return true;
|
|
return false;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
inline bool PolygonSimplifier::Vertex::checkConnectivity()
|
|
{
|
|
VL_CHECK( mCollapseVertex )
|
|
VL_CHECK( !mCollapseVertex->removed() )
|
|
// check connectivity consistency
|
|
for(int ivert=0; ivert<adjacentVerticesCount(); ++ivert)
|
|
{
|
|
Vertex* adj = mAdjacentVerts[ivert];
|
|
if ( adj->removed() )
|
|
return false;
|
|
if( std::find(adj->mAdjacentVerts.begin(), adj->mAdjacentVerts.end(), this) == adj->mAdjacentVerts.end() )
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
// Triangle
|
|
//-----------------------------------------------------------------------------
|
|
inline PolygonSimplifier::QErr PolygonSimplifier::Triangle::computeQErr() const
|
|
{
|
|
dvec3 n = (dvec3)normal();
|
|
double d = -dot((dvec3)vertex(0)->position(), n);
|
|
QErr qerr(n, d, computeArea() * (1.0 / 3.0) );
|
|
return qerr;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
inline fvec3 PolygonSimplifier::Triangle::computePotentialNormal(const Vertex* oldv, const Vertex* newv) const
|
|
{
|
|
fvec3 a = (mVertices[0]->mPosition == oldv->mPosition ? newv->mPosition : mVertices[0]->mPosition);
|
|
fvec3 b = (mVertices[1]->mPosition == oldv->mPosition ? newv->mPosition : mVertices[1]->mPosition) - a;
|
|
fvec3 c = (mVertices[2]->mPosition == oldv->mPosition ? newv->mPosition : mVertices[2]->mPosition) - a;
|
|
|
|
fvec3 n = cross(b,c);
|
|
n.normalize();
|
|
return n;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
inline bool PolygonSimplifier::Triangle::checkTriangle() const
|
|
{
|
|
bool ok = true;
|
|
ok &= !mVertices[0]->removed(); VL_CHECK(ok)
|
|
ok &= !mVertices[1]->removed(); VL_CHECK(ok)
|
|
ok &= !mVertices[2]->removed(); VL_CHECK(ok)
|
|
ok &= mVertices[0] != mVertices[1]; VL_CHECK(ok)
|
|
ok &= mVertices[0] != mVertices[2]; VL_CHECK(ok)
|
|
ok &= mVertices[1] != mVertices[2]; VL_CHECK(ok)
|
|
return ok;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
inline bool PolygonSimplifier::Triangle::hasVertex(const Vertex*v) const
|
|
{
|
|
return mVertices[0] == v || mVertices[1] == v || mVertices[2] == v;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
inline float PolygonSimplifier::Triangle::computePotentialArea(const Vertex* oldv, const Vertex* newv) const
|
|
{
|
|
fvec3 A = (mVertices[0]->mPosition == oldv->mPosition ? newv->mPosition : mVertices[0]->mPosition);
|
|
fvec3 B = (mVertices[1]->mPosition == oldv->mPosition ? newv->mPosition : mVertices[1]->mPosition) - A;
|
|
fvec3 C = (mVertices[2]->mPosition == oldv->mPosition ? newv->mPosition : mVertices[2]->mPosition) - A;
|
|
|
|
float base = 0.0f;
|
|
float height = 0.0f;
|
|
fvec3 AC = C-A;
|
|
fvec3 AB = B-A;
|
|
base = AB.length();
|
|
AB = AB * (1.0f / base); // normalize
|
|
fvec3 h = vl::dot(AC,AB) * AB + A;
|
|
height = (C-h).length();
|
|
|
|
return base * height * 0.5f;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
inline float PolygonSimplifier::Triangle::computeArea() const
|
|
{
|
|
const fvec3& A = mVertices[0]->mPosition;
|
|
const fvec3& B = mVertices[1]->mPosition;
|
|
const fvec3& C = mVertices[2]->mPosition;
|
|
|
|
float base = 0.0f;
|
|
float height = 0.0f;
|
|
fvec3 AC = C-A;
|
|
fvec3 AB = B-A;
|
|
base = AB.length();
|
|
if (!base)
|
|
return 0;
|
|
AB = AB * (1.0f / base); // normalize
|
|
fvec3 h = vl::dot(AC,AB) * AB + A;
|
|
height = (C-h).length();
|
|
// indeterminate cases
|
|
VL_CHECK( base == base )
|
|
VL_CHECK( height == height )
|
|
return base * height * 0.5f;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
inline void PolygonSimplifier::Triangle::computeNormal()
|
|
{
|
|
const fvec3& a = mVertices[0]->mPosition;
|
|
fvec3 b = mVertices[1]->mPosition - a;
|
|
fvec3 c = mVertices[2]->mPosition - a;
|
|
mNormal = cross(b,c);
|
|
mNormal.normalize();
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
inline void PolygonSimplifier::Triangle::replaceVertex( Vertex* oldv, Vertex* newv )
|
|
{
|
|
// becomes a degenerate triangle if "newv" is already here
|
|
mRemoved = hasVertex(newv);
|
|
if (mRemoved)
|
|
{
|
|
//mVertices[0]->removeIncidentTriangle(this);
|
|
//mVertices[1]->removeIncidentTriangle(this);
|
|
//mVertices[2]->removeIncidentTriangle(this);
|
|
}
|
|
else
|
|
{
|
|
if (mVertices[0] == oldv) mVertices[0] = newv;
|
|
if (mVertices[1] == oldv) mVertices[1] = newv;
|
|
if (mVertices[2] == oldv) mVertices[2] = newv;
|
|
VL_CHECK( !mVertices[0]->mRemoved )
|
|
VL_CHECK( !mVertices[1]->mRemoved )
|
|
VL_CHECK( !mVertices[2]->mRemoved )
|
|
}
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
inline void PolygonSimplifier::collapse(Vertex* v)
|
|
{
|
|
VL_CHECK(!v->mRemoved)
|
|
VL_CHECK(v->mCollapseVertex)
|
|
VL_CHECK( !v->mCollapseVertex->mRemoved )
|
|
#ifndef NDEBUG
|
|
v->checkConnectivity();
|
|
// check connectivity consistency
|
|
for(int ivert=0; ivert<v->adjacentVerticesCount(); ++ivert)
|
|
{
|
|
VL_CHECK( v->mAdjacentVerts[ivert]->checkConnectivity() )
|
|
}
|
|
#endif
|
|
|
|
v->mRemoved = true;
|
|
|
|
v->mCollapseVertex->mPosition = v->mCollapsePosition;
|
|
v->mCollapseVertex->mQErr += v->mQErr;
|
|
|
|
for(int itri=0; itri<v->incidentTrianglesCount(); ++itri)
|
|
{
|
|
VL_CHECK(!v->mIncidentTriangles[itri]->mRemoved)
|
|
|
|
// - point the triangle to use the new mCollapseVertex instead of "this"
|
|
// - flags for removal
|
|
v->mIncidentTriangles[itri]->replaceVertex( v, v->mCollapseVertex );
|
|
|
|
// pass this's adjacent triangles to mCollapseVertex
|
|
if (!v->mIncidentTriangles[itri]->mRemoved)
|
|
{
|
|
// check that it does not have it already, the ones in common have been marked as "removed"
|
|
VL_CHECK( !v->mCollapseVertex->isIncidentTriangle( v->mIncidentTriangles[itri] ) )
|
|
v->mCollapseVertex->mIncidentTriangles.push_back( v->mIncidentTriangles[itri] );
|
|
}
|
|
}
|
|
|
|
// erase removed triangles from its adjacent vertices (including mCollapseVertex)
|
|
for(int ivert=0; ivert<v->adjacentVerticesCount(); ++ivert)
|
|
v->mAdjacentVerts[ivert]->discardRemovedTriangles();
|
|
|
|
// update adjacent vertices of all the vertices adjacent to this (including mCollapseVertex)
|
|
for(int ivert=0; ivert<v->adjacentVerticesCount(); ++ivert)
|
|
{
|
|
#if 1
|
|
// this is correct and more robust since marks as removed the vertices with 0 triangles
|
|
v->mAdjacentVerts[ivert]->computeAdjacentVertices();
|
|
#else
|
|
... this is not correct since does not mark as removed the vertices that remain without triangles ...
|
|
mAdjacentVerts[ivert]->removeAdjacentVertex(this);
|
|
mAdjacentVerts[ivert]->addAdjacentVertex(mCollapseVertex);
|
|
mCollapseVertex->addAdjacentVertex(mAdjacentVerts[ivert]);
|
|
#endif
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
for(int ivert=0; ivert<v->mCollapseVertex->adjacentVerticesCount(); ++ivert)
|
|
{
|
|
VL_CHECK( v->mCollapseVertex->adjacentVertex(ivert)->checkTriangles() )
|
|
}
|
|
|
|
// and outside even this vertex is removed.
|
|
if ( v->mCollapseVertex->removed() )
|
|
{
|
|
VL_CHECK( v->mCollapseVertex->mIncidentTriangles.empty() )
|
|
VL_CHECK( v->mCollapseVertex->mAdjacentVerts.empty() )
|
|
}
|
|
#endif
|
|
|
|
// --- now we work on mCollapseVertex ---
|
|
|
|
if ( !quick() )
|
|
{
|
|
// update the normals, used to compute anti-folding
|
|
for(int itri=0; itri<v->mCollapseVertex->incidentTrianglesCount(); ++itri)
|
|
{
|
|
VL_CHECK( !v->mCollapseVertex->mIncidentTriangles[itri]->removed() )
|
|
VL_CHECK( v->mCollapseVertex->mIncidentTriangles[itri]->checkTriangle() )
|
|
v->mCollapseVertex->mIncidentTriangles[itri]->computeNormal();
|
|
}
|
|
}
|
|
|
|
VL_CHECK( !v->mCollapseVertex->isAdjacentVertex(v) )
|
|
|
|
// disconnect this vertex
|
|
v->mIncidentTriangles.clear();
|
|
v->mAdjacentVerts.clear();
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
// compute collapse cost and vertex
|
|
inline void PolygonSimplifier::computeCollapseInfo(Vertex* v)
|
|
{
|
|
VL_CHECK(!v->mRemoved)
|
|
if(v->mRemoved)
|
|
return;
|
|
|
|
// choose the edge with minimum cost
|
|
|
|
// intialize with a very high cost
|
|
v->mCollapseCost = 1.0e+38f;
|
|
v->mCollapseVertex = NULL;
|
|
VL_CHECK( v->adjacentVerticesCount() )
|
|
for(int ivert=0; ivert<v->adjacentVerticesCount(); ++ivert)
|
|
{
|
|
VL_CHECK(!v->mAdjacentVerts[ivert]->mRemoved)
|
|
|
|
double cost = 0.0;
|
|
dvec3 solution;
|
|
if (quick())
|
|
{
|
|
QErr qe = v->qerr();
|
|
qe += v->mAdjacentVerts[ivert]->qerr();
|
|
// find the best solution
|
|
solution = (dvec3)v->position();
|
|
solution += (dvec3)v->mAdjacentVerts[ivert]->position();
|
|
solution *= 0.5;
|
|
cost = qe.evaluate( solution );
|
|
}
|
|
else
|
|
{
|
|
QErr qe = v->qerr();
|
|
qe += v->mAdjacentVerts[ivert]->qerr();
|
|
bool analytic_ok = qe.analyticSolution(solution);
|
|
if ( analytic_ok )
|
|
{
|
|
cost = qe.evaluate(solution);
|
|
VL_CHECK(cost < 1e+38)
|
|
}
|
|
else
|
|
{
|
|
dvec3 a = (dvec3)v->position();
|
|
dvec3 b = (dvec3)v->mAdjacentVerts[ivert]->position();
|
|
dvec3 c = (a+b) * 0.5;
|
|
double ae = qe.evaluate(a);
|
|
double be = qe.evaluate(b);
|
|
double ce = qe.evaluate(c);
|
|
if (ae < be && ae < ce)
|
|
{
|
|
solution = a;
|
|
cost = ae;
|
|
}
|
|
else
|
|
if (be < ae && be < ce)
|
|
{
|
|
solution = b;
|
|
cost = be;
|
|
}
|
|
else
|
|
{
|
|
solution = c;
|
|
cost = ce;
|
|
}
|
|
VL_CHECK(cost < 1e+38)
|
|
}
|
|
|
|
int degenerate_count = 0;
|
|
for( int itri=0; itri<v->incidentTrianglesCount() && !degenerate_count; ++itri )
|
|
{
|
|
// triangle to be removed
|
|
if ( v->incidentTriangle(itri)->hasVertex(v->mAdjacentVerts[ivert]) )
|
|
continue;
|
|
|
|
Vertex* edgev[] = { NULL, NULL };
|
|
if ( v == v->incidentTriangle(itri)->vertex(0) )
|
|
{
|
|
edgev[0] = v->incidentTriangle(itri)->vertex(1);
|
|
edgev[1] = v->incidentTriangle(itri)->vertex(2);
|
|
}
|
|
else
|
|
if ( v == v->incidentTriangle(itri)->vertex(1) )
|
|
{
|
|
edgev[0] = v->incidentTriangle(itri)->vertex(0);
|
|
edgev[1] = v->incidentTriangle(itri)->vertex(2);
|
|
}
|
|
else
|
|
if ( v == v->incidentTriangle(itri)->vertex(2) )
|
|
{
|
|
edgev[0] = v->incidentTriangle(itri)->vertex(0);
|
|
edgev[1] = v->incidentTriangle(itri)->vertex(1);
|
|
}
|
|
|
|
fvec3 edge = (edgev[1]->position() - edgev[0]->position());
|
|
fvec3 n = cross( edge, v->incidentTriangle(itri)->normal() );
|
|
n.normalize();
|
|
float d1 = dot( v->position() - edgev[0]->position(), n );
|
|
float d2 = dot( (fvec3)solution - edgev[0]->position(), n );
|
|
|
|
if (d1 * d2 < 0)
|
|
++degenerate_count;
|
|
}
|
|
|
|
// controlla i triangoli intorno a v->mAdjacentVerts[ivert]
|
|
Vertex* u = v->mAdjacentVerts[ivert];
|
|
for( int itri=0; itri<u->incidentTrianglesCount() && !degenerate_count; ++itri )
|
|
{
|
|
// triangle to be removed
|
|
if ( u->incidentTriangle(itri)->hasVertex(v) )
|
|
continue;
|
|
|
|
Vertex* edgev[] = { NULL, NULL };
|
|
if ( u == u->incidentTriangle(itri)->vertex(0) )
|
|
{
|
|
edgev[0] = u->incidentTriangle(itri)->vertex(1);
|
|
edgev[1] = u->incidentTriangle(itri)->vertex(2);
|
|
}
|
|
else
|
|
if ( u == u->incidentTriangle(itri)->vertex(1) )
|
|
{
|
|
edgev[0] = u->incidentTriangle(itri)->vertex(0);
|
|
edgev[1] = u->incidentTriangle(itri)->vertex(2);
|
|
}
|
|
else
|
|
if ( u == u->incidentTriangle(itri)->vertex(2) )
|
|
{
|
|
edgev[0] = u->incidentTriangle(itri)->vertex(0);
|
|
edgev[1] = u->incidentTriangle(itri)->vertex(1);
|
|
}
|
|
|
|
fvec3 edge = (edgev[1]->position() - edgev[0]->position());
|
|
fvec3 n = cross( edge, u->incidentTriangle(itri)->normal() );
|
|
n.normalize();
|
|
float d1 = dot( u->position() - edgev[0]->position(), n );
|
|
float d2 = dot( (fvec3)solution - edgev[0]->position(), n );
|
|
|
|
if (d1 * d2 < 0)
|
|
++degenerate_count;
|
|
}
|
|
|
|
// non acceptable solution, assign very high cost
|
|
if (degenerate_count)
|
|
cost = 1.0e+37f;
|
|
}
|
|
|
|
// to correctly simplify planar and cylindrical regions
|
|
cost += ((dvec3)v->position() - solution).length() * 1.0e-12;
|
|
|
|
// check indeterminate case
|
|
VL_CHECK( cost == cost )
|
|
if ( cost < v->mCollapseCost )
|
|
{
|
|
v->mCollapseCost = (float)cost;
|
|
v->mCollapseVertex = v->mAdjacentVerts[ivert];
|
|
v->mCollapsePosition = (fvec3)solution;
|
|
}
|
|
}
|
|
|
|
VL_CHECK( v->mCollapseVertex )
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
}
|
|
|
|
#endif
|