#include #include #include #include #include #include "hacdMeshDecimator.h" namespace HACD { MeshDecimator::MeshDecimator(void) { m_triangles = 0; m_points = 0; m_nPoints = 0; m_nInitialTriangles = 0; m_nVertices = 0; m_nTriangles = 0; m_nEdges = 0; m_trianglesTags = 0; m_ecolManifoldConstraint = true; m_callBack = 0; } MeshDecimator::~MeshDecimator(void) { ReleaseMemory(); } void MeshDecimator::ReleaseMemory() { delete [] m_trianglesTags; std::vector< MDVertex > emptyVertices(0); m_vertices.swap(emptyVertices); std::vector emptyEdges(0); m_edges.swap(emptyEdges); m_pqueue = std::priority_queue< MDEdgePriorityQueue, std::vector, std::less >(); m_triangles = 0; m_points = 0; m_nPoints = 0; m_nInitialTriangles = 0; m_nVertices = 0; m_nTriangles = 0; m_nEdges = 0; m_trianglesTags = 0; } void MeshDecimator::Initialize(size_t nVertices, size_t nTriangles, Vec3 * points, Vec3 * triangles) { m_nVertices = nVertices; m_nTriangles = nTriangles; m_nInitialTriangles = nTriangles; m_points = points; m_nPoints = nVertices; m_triangles = triangles; m_trianglesTags = new bool[m_nTriangles]; m_edges.reserve(3*m_nTriangles); m_vertices.resize(m_nVertices); for(size_t v = 0; v < m_nVertices; ++v) { m_vertices[v].m_tag = true; } long tri[3]; MDEdge edge; edge.m_tag = true; edge.m_onBoundary = true; long nEdges = 0; long idEdge; long nTris = static_cast(m_nTriangles); for(long t = 0; t < nTris; ++t) { tri[0] = m_triangles[t].X(); tri[1] = m_triangles[t].Y(); tri[2] = m_triangles[t].Z(); m_trianglesTags[t] = true; for(int k = 0; k < 3; ++k) { edge.m_v1 = tri[k]; edge.m_v2 = tri[(k+1)%3]; m_vertices[edge.m_v1].m_triangles.Insert(t); idEdge = GetEdge(edge.m_v1, edge.m_v2); if (idEdge == -1) { m_edges.push_back(edge); m_vertices[edge.m_v1].m_edges.Insert(nEdges); m_vertices[edge.m_v2].m_edges.Insert(nEdges); ++nEdges; } else { m_edges[idEdge].m_onBoundary = false; } } } m_nEdges = static_cast(nEdges); for(size_t v = 0; v < m_nVertices; ++v) { m_vertices[v].m_onBoundary = false; for(size_t itE = 0; itE < m_vertices[v].m_edges.Size(); ++itE) { idEdge = m_vertices[v].m_edges[itE]; if (m_edges[idEdge].m_onBoundary) { m_vertices[v].m_onBoundary = true; break; } } } } long MeshDecimator::GetTriangle(long v1, long v2, long v3) const { long i, j, k; long idTriangle; for(size_t it = 0; it < m_vertices[v1].m_triangles.Size(); ++it) { idTriangle = m_vertices[v1].m_triangles[it]; i = m_triangles[idTriangle].X(); j = m_triangles[idTriangle].Y(); k = m_triangles[idTriangle].Z(); if ( (i==v1 && j==v2 && k==v3) || (i==v1 && j==v3 && k==v2) || (i==v2 && j==v1 && k==v3) || (i==v2 && j==v3 && k==v1) || (i==v3 && j==v2 && k==v1) || (i==v3 && j==v1 && k==v2) ) { return idTriangle; } } return -1; } long MeshDecimator::GetEdge(long v1, long v2) const { long idEdge; for(size_t it = 0; it < m_vertices[v1].m_edges.Size(); ++it) { idEdge = m_vertices[v1].m_edges[it]; if ( (m_edges[idEdge].m_v1==v1 && m_edges[idEdge].m_v2==v2) || (m_edges[idEdge].m_v1==v2 && m_edges[idEdge].m_v2==v1) ) { return idEdge; } } return -1; } void MeshDecimator::EdgeCollapse(long v1, long v2) { long u, w; int shift; long idTriangle; for(size_t itT = 0; itT < m_vertices[v2].m_triangles.Size(); ++itT) { idTriangle = m_vertices[v2].m_triangles[itT]; if (m_triangles[idTriangle].X() == v2) { shift = 0; u = m_triangles[idTriangle].Y(); w = m_triangles[idTriangle].Z(); } else if (m_triangles[idTriangle].Y() == v2) { shift = 1; u = m_triangles[idTriangle].X(); w = m_triangles[idTriangle].Z(); } else { shift = 2; u = m_triangles[idTriangle].X(); w = m_triangles[idTriangle].Y(); } if ((u == v1) || (w == v1)) { m_trianglesTags[idTriangle] = false; m_vertices[u].m_triangles.Erase(idTriangle); m_vertices[w].m_triangles.Erase(idTriangle); m_nTriangles--; } else if (GetTriangle(v1, u, w) == -1) { m_vertices[v1].m_triangles.Insert(idTriangle); m_triangles[idTriangle][shift] = v1; } else { m_trianglesTags[idTriangle] = false; m_vertices[u].m_triangles.Erase(idTriangle); m_vertices[w].m_triangles.Erase(idTriangle); m_nTriangles--; } } long idEdge = 0; for(size_t itE = 0; itE < m_vertices[v2].m_edges.Size(); ++itE) { idEdge = m_vertices[v2].m_edges[itE]; w = (m_edges[idEdge].m_v1 == v2)? m_edges[idEdge].m_v2 : m_edges[idEdge].m_v1; if (w==v1) { m_edges[idEdge].m_tag = false; m_vertices[w].m_edges.Erase(idEdge); m_nEdges--; } else if ( GetEdge(v1, w) == -1) { if (m_edges[idEdge].m_v1 == v2) m_edges[idEdge].m_v1 = v1; else m_edges[idEdge].m_v2 = v1; m_vertices[v1].m_edges.Insert(idEdge); } else { m_edges[idEdge].m_tag = false; m_vertices[w].m_edges.Erase(idEdge); m_nEdges--; } } m_vertices[v2].m_tag = false; m_nVertices--; // update boundary edges SArray incidentVertices; incidentVertices.PushBack(v1); for(size_t itE = 0; itE < m_vertices[v1].m_edges.Size(); ++itE) { incidentVertices.PushBack((m_edges[idEdge].m_v1!= v1)?m_edges[idEdge].m_v1:m_edges[idEdge].m_v2); idEdge = m_vertices[v1].m_edges[itE]; m_edges[idEdge].m_onBoundary = (IsBoundaryEdge(m_edges[idEdge].m_v1, m_edges[idEdge].m_v2) != -1); } // update boundary vertices long idVertex; for(size_t itV = 0; itV < incidentVertices.Size(); ++itV) { idVertex = incidentVertices[itV]; m_vertices[idVertex].m_onBoundary = false; for(size_t itE = 0; itE < m_vertices[idVertex].m_edges.Size(); ++itE) { idEdge = m_vertices[idVertex].m_edges[itE]; if (m_edges[idEdge].m_onBoundary) { m_vertices[idVertex].m_onBoundary = true; break; } } } } long MeshDecimator::IsBoundaryEdge(long v1, long v2) const { long commonTri = -1; long itTriangle1, itTriangle2; for(size_t itT1 = 0; itT1 < m_vertices[v1].m_triangles.Size(); ++itT1) { itTriangle1 = m_vertices[v1].m_triangles[itT1]; for(size_t itT2 = 0; itT2 < m_vertices[v2].m_triangles.Size(); ++itT2) { itTriangle2 = m_vertices[v2].m_triangles[itT2]; if (itTriangle1 == itTriangle2) { if (commonTri == -1) { commonTri = itTriangle1; } else { return -1; } } } } return commonTri; } bool MeshDecimator::IsBoundaryVertex(long v) const { long idEdge; for(size_t itE = 0; itE < m_vertices[v].m_edges.Size(); ++itE) { idEdge = m_vertices[v].m_edges[itE]; if ( IsBoundaryEdge(m_edges[idEdge].m_v1, m_edges[idEdge].m_v2) != -1) return true; } return false; } void MeshDecimator::GetMeshData(Vec3 * points, Vec3 * triangles) const { long * map = new long [m_nPoints]; long counter = 0; for (size_t v = 0; v < m_nPoints; ++v) { if ( m_vertices[v].m_tag ) { points[counter] = m_points[v]; map[v] = counter++; } } counter = 0; for (size_t t = 0; t < m_nInitialTriangles; ++t) { if ( m_trianglesTags[t] ) { triangles[counter].X() = map[m_triangles[t].X()]; triangles[counter].Y() = map[m_triangles[t].Y()]; triangles[counter].Z() = map[m_triangles[t].Z()]; counter++; } } delete [] map; } void MeshDecimator::InitializeQEM() { Vec3 coordMin = m_points[0]; Vec3 coordMax = m_points[0]; Vec3 coord; for (size_t p = 1; p < m_nPoints ; ++p) { coord = m_points[p]; if (coordMin.X() > coord.X()) coordMin.X() = coord.X(); if (coordMin.Y() > coord.Y()) coordMin.Y() = coord.Y(); if (coordMin.Z() > coord.Z()) coordMin.Z() = coord.Z(); if (coordMax.X() < coord.X()) coordMax.X() = coord.X(); if (coordMax.Y() < coord.Y()) coordMax.Y() = coord.Y(); if (coordMax.Z() < coord.Z()) coordMax.Z() = coord.Z(); } coordMax -= coordMin; m_diagBB = coordMax.GetNorm(); long i, j, k; Vec3 n; Float d = 0; Float area = 0; for(size_t v = 0; v < m_nPoints; ++v) { memset(m_vertices[v].m_Q, 0, 10 * sizeof(Float)); long idTriangle; for(size_t itT = 0; itT < m_vertices[v].m_triangles.Size(); ++itT) { idTriangle = m_vertices[v].m_triangles[itT]; i = m_triangles[idTriangle].X(); j = m_triangles[idTriangle].Y(); k = m_triangles[idTriangle].Z(); n = (m_points[j] - m_points[i])^(m_points[k] - m_points[i]); area = n.GetNorm(); n.Normalize(); d = - (m_points[v] * n); m_vertices[v].m_Q[0] += area * (n.X() * n.X()); m_vertices[v].m_Q[1] += area * (n.X() * n.Y()); m_vertices[v].m_Q[2] += area * (n.X() * n.Z()); m_vertices[v].m_Q[3] += area * (n.X() * d); m_vertices[v].m_Q[4] += area * (n.Y() * n.Y()); m_vertices[v].m_Q[5] += area * (n.Y() * n.Z()); m_vertices[v].m_Q[6] += area * (n.Y() * d); m_vertices[v].m_Q[7] += area * (n.Z() * n.Z()); m_vertices[v].m_Q[8] += area * (n.Z() * d); m_vertices[v].m_Q[9] += area * (d * d); } } Vec3 u1, u2; const Float w = static_cast(1000); long t, v1, v2, v3; for(size_t e = 0; e < m_edges.size(); ++e) { v1 = m_edges[e].m_v1; v2 = m_edges[e].m_v2; t = IsBoundaryEdge(v1, v2); if (t != -1) { if (m_triangles[t].X() != v1 && m_triangles[t].X() != v2) v3 = m_triangles[t].X(); else if (m_triangles[t].Y() != v1 && m_triangles[t].Y() != v2) v3 = m_triangles[t].Y(); else v3 = m_triangles[t].Z(); u1 = m_points[v2] - m_points[v1]; u2 = m_points[v3] - m_points[v1]; area = w * (u1^u2).GetNorm(); u1.Normalize(); n = u2 - (u2 * u1) * u1; n.Normalize(); d = - (m_points[v1] * n); m_vertices[v1].m_Q[0] += area * (n.X() * n.X()); m_vertices[v1].m_Q[1] += area * (n.X() * n.Y()); m_vertices[v1].m_Q[2] += area * (n.X() * n.Z()); m_vertices[v1].m_Q[3] += area * (n.X() * d); m_vertices[v1].m_Q[4] += area * (n.Y() * n.Y()); m_vertices[v1].m_Q[5] += area * (n.Y() * n.Z()); m_vertices[v1].m_Q[6] += area * (n.Y() * d); m_vertices[v1].m_Q[7] += area * (n.Z() * n.Z()); m_vertices[v1].m_Q[8] += area * (n.Z() * d); m_vertices[v1].m_Q[9] += area * (d * d); d = - (m_points[v2] * n); m_vertices[v2].m_Q[0] += area * (n.X() * n.X()); m_vertices[v2].m_Q[1] += area * (n.X() * n.Y()); m_vertices[v2].m_Q[2] += area * (n.X() * n.Z()); m_vertices[v2].m_Q[3] += area * (n.X() * d); m_vertices[v2].m_Q[4] += area * (n.Y() * n.Y()); m_vertices[v2].m_Q[5] += area * (n.Y() * n.Z()); m_vertices[v2].m_Q[6] += area * (n.Y() * d); m_vertices[v2].m_Q[7] += area * (n.Z() * n.Z()); m_vertices[v2].m_Q[8] += area * (n.Z() * d); m_vertices[v2].m_Q[9] += area * (d * d); } } } void MeshDecimator::InitializePriorityQueue() { double progressOld = -1.0; double progress = 0.0; char msg[1024]; double ptgStep = 1.0; long v1, v2; MDEdgePriorityQueue pqEdge; size_t nE = m_edges.size(); for(size_t e = 0; e < nE; ++e) { progress = e * 100.0 / nE; if (fabs(progress-progressOld) > ptgStep && m_callBack) { sprintf(msg, "%3.2f %% \t \t \r", progress); (*m_callBack)(msg, progress, 0.0, m_nVertices); progressOld = progress; } if (m_edges[e].m_tag) { v1 = m_edges[e].m_v1; v2 = m_edges[e].m_v2; if ( (!m_ecolManifoldConstraint) || (ManifoldConstraint(v1, v2))) { pqEdge.m_qem = m_edges[e].m_qem = ComputeEdgeCost(v1, v2, m_edges[e].m_pos); pqEdge.m_name = static_cast(e); m_pqueue.push(pqEdge); } } } } double MeshDecimator::ComputeEdgeCost(long v1, long v2, Vec3 & newPos) const { double Q[10]; double M[12]; Vec3 pos; for(int i = 0; i < 10; ++i) Q[i] = m_vertices[v1].m_Q[i] + m_vertices[v2].m_Q[i]; M[0] = Q[0]; // (0, 0) M[1] = Q[1]; // (0, 1) M[2] = Q[2]; // (0, 2) M[3] = Q[3]; // (0, 3) M[4] = Q[1]; // (1, 0) M[5] = Q[4]; // (1, 1) M[6] = Q[5]; // (1, 2) M[7] = Q[6]; // (1, 3) M[8] = Q[2]; // (2, 0) M[9] = Q[5]; // (2, 1) M[10] = Q[7]; // (2, 2); M[11] = Q[8]; // (2, 3); double det = M[0] * M[5] * M[10] + M[1] * M[6] * M[8] + M[2] * M[4] * M[9] - M[0] * M[6] * M[9] - M[1] * M[4] * M[10]- M[2] * M[5] * M[8]; if (det != 0.0) { double d = 1.0 / det; pos.X() = d * (M[1]*M[7]*M[10] + M[2]*M[5]*M[11] + M[3]*M[6]*M[9] -M[1]*M[6]*M[11] - M[2]*M[7]*M[9] - M[3]*M[5]*M[10]); pos.Y() = d * (M[0]*M[6]*M[11] + M[2]*M[7]*M[8] + M[3]*M[4]*M[10] -M[0]*M[7]*M[10] - M[2]*M[4]*M[11] - M[3]*M[6]*M[8]); pos.Z() = d * (M[0]*M[7]*M[9] + M[1]*M[4]*M[11] + M[3]*M[5]*M[8] -M[0]*M[5]*M[11] - M[1]*M[7]*M[8] - M[3]*M[4]*M[9]); newPos.X() = static_cast(pos.X()); newPos.Y() = static_cast(pos.Y()); newPos.Z() = static_cast(pos.Z()); } else { const Float w = static_cast(0.5f); newPos = w * m_points[v1] + w * m_points[v2]; pos.X() = static_cast(newPos.X()); pos.Y() = static_cast(newPos.Y()); pos.Z() = static_cast(newPos.Z()); } double qem = pos.X() * (Q[0] * pos.X() + Q[1] * pos.Y() + Q[2] * pos.Z() + Q[3]) + pos.Y() * (Q[1] * pos.X() + Q[4] * pos.Y() + Q[5] * pos.Z() + Q[6]) + pos.Z() * (Q[2] * pos.X() + Q[5] * pos.Y() + Q[7] * pos.Z() + Q[8]) + (Q[3] * pos.X() + Q[6] * pos.Y() + Q[8] * pos.Z() + Q[9]) ; Vec3 d1; Vec3 d2; Vec3 n1; Vec3 n2; Vec3 oldPosV1 = m_points[v1]; Vec3 oldPosV2 = m_points[v2]; SArray triangles = m_vertices[v1].m_triangles; long idTriangle; for(size_t itT = 0; itT < m_vertices[v2].m_triangles.Size(); ++itT) { idTriangle = m_vertices[v2].m_triangles[itT]; triangles.Insert(idTriangle); } long a[3]; for(size_t itT = 0; itT != triangles.Size(); ++itT) { idTriangle = triangles[itT]; a[0] = m_triangles[idTriangle].X(); a[1] = m_triangles[idTriangle].Y(); a[2] = m_triangles[idTriangle].Z(); d1 = m_points[a[1]] - m_points[a[0]]; d2 = m_points[a[2]] - m_points[a[0]]; n1 = d1^d2; m_points[v1] = newPos; m_points[v2] = newPos; d1 = m_points[a[1]] - m_points[a[0]]; d2 = m_points[a[2]] - m_points[a[0]]; n2 = d1^d2; m_points[v1] = oldPosV1; m_points[v2] = oldPosV2; n1.Normalize(); n2.Normalize(); if (n1*n2 < 0.0) { return std::numeric_limits::max(); } } if ( m_ecolManifoldConstraint && !ManifoldConstraint(v1, v2)) { return std::numeric_limits::max(); } return qem; } bool MeshDecimator::ManifoldConstraint(long v1, long v2) const { std::set vertices; long a, b; long idEdge1; long idEdge2; long idEdgeV1V2 = 0; for(size_t itE1 = 0; itE1 < m_vertices[v1].m_edges.Size(); ++itE1) { idEdge1 = m_vertices[v1].m_edges[itE1]; a = (m_edges[idEdge1].m_v1 == v1) ? m_edges[idEdge1].m_v2 : m_edges[idEdge1].m_v1; vertices.insert(a); if (a != v2) { for(size_t itE2 = 0; itE2 < m_vertices[v2].m_edges.Size(); ++itE2) { idEdge2 = m_vertices[v2].m_edges[itE2]; b = (m_edges[idEdge2].m_v1 == v2) ? m_edges[idEdge2].m_v2 : m_edges[idEdge2].m_v1; vertices.insert(b); if ( a==b ) { if (GetTriangle(v1, v2, a) == -1) { return false; } } } } else { idEdgeV1V2 = idEdge1; } } if (vertices.size() <= 4 || ( m_vertices[v1].m_onBoundary && m_vertices[v2].m_onBoundary && !m_edges[idEdgeV1V2].m_onBoundary)) { return false; } return true; } bool MeshDecimator::EdgeCollapse(double & qem) { MDEdgePriorityQueue currentEdge; long v1, v2; bool done = false; do { done = false; if (m_pqueue.size() == 0) { done = true; break; } else { currentEdge = m_pqueue.top(); m_pqueue.pop(); } } while ( (!m_edges[currentEdge.m_name].m_tag) || (m_edges[currentEdge.m_name].m_qem != currentEdge.m_qem)); if (done) return false; v1 = m_edges[currentEdge.m_name].m_v1; v2 = m_edges[currentEdge.m_name].m_v2; qem = currentEdge.m_qem; EdgeCollapse(v1, v2); m_points[v1] = m_edges[currentEdge.m_name].m_pos ; for(int k = 0; k < 10; k++) m_vertices[v1].m_Q[k] += m_vertices[v2].m_Q[k]; // Update priority queue long idEdge; long a, b; SArray incidentVertices; for(size_t itE = 0; itE < m_vertices[v1].m_edges.Size(); ++itE) { idEdge = m_vertices[v1].m_edges[itE]; a = m_edges[idEdge].m_v1; b = m_edges[idEdge].m_v2; incidentVertices.PushBack((a != v1)?a:b); MDEdgePriorityQueue pqEdge; pqEdge.m_qem = m_edges[idEdge].m_qem = ComputeEdgeCost(a, b, m_edges[idEdge].m_pos); pqEdge.m_name = idEdge; m_pqueue.push(pqEdge); } long idVertex; for(size_t itV = 0; itV< incidentVertices.Size(); ++itV) { idVertex = incidentVertices[itV]; for(size_t itE = 0; itE < m_vertices[idVertex].m_edges.Size(); ++itE) { idEdge = m_vertices[idVertex].m_edges[itE]; a = m_edges[idEdge].m_v1; b = m_edges[idEdge].m_v2; if ( a!=v1 && b!=v1) { MDEdgePriorityQueue pqEdge; pqEdge.m_qem = m_edges[idEdge].m_qem = ComputeEdgeCost(a, b, m_edges[idEdge].m_pos); pqEdge.m_name = idEdge; m_pqueue.push(pqEdge); } } } return true; } bool MeshDecimator::Decimate(size_t targetNVertices, size_t targetNTriangles, double targetError) { double qem = 0.0; double progressOld = -1.0; double progress = 0.0; char msg[1024]; double ptgStep = 1.0; if (m_callBack) { std::ostringstream msg; msg << "+ Mesh" << std::endl; msg << "\t # vertices \t" << m_nPoints << std::endl; msg << "\t # triangles \t" << m_nTriangles << std::endl; msg << "+ Parameters" << std::endl; msg << "\t target # of vertices \t" << targetNVertices << std::endl; msg << "\t target # of triangles \t" << targetNTriangles << std::endl; msg << "\t QEM \t" << targetError << std::endl; (*m_callBack)(msg.str().c_str(), 0.0, 0.0, m_nPoints); } if (m_callBack) (*m_callBack)("+ Initialize QEM \n", 0.0, 0.0, m_nPoints); InitializeQEM(); if (m_callBack) (*m_callBack)("+ Initialize priority queue \n", 0.0, 0.0, m_nPoints); InitializePriorityQueue(); if (m_callBack) (*m_callBack)("+ Simplification \n", 0.0, 0.0, m_nPoints); double invDiag2 = 1.0 / (m_diagBB * m_diagBB); while((m_pqueue.size() > 0) && (m_nEdges > 0) && (m_nVertices > targetNVertices) && (m_nTriangles > targetNTriangles) && (qem < targetError)) { progress = 100.0 - m_nVertices * 100.0 / m_nPoints; if (fabs(progress-progressOld) > ptgStep && m_callBack) { sprintf(msg, "%3.2f %% V = %lu \t QEM = %f \t \t \r", progress, static_cast(m_nVertices), sqrt(qem)); (*m_callBack)(msg, progress, qem, m_nVertices); progressOld = progress; } if (!EdgeCollapse(qem)) break; qem *= invDiag2; } if (m_callBack) { std::ostringstream msg; msg << "+ Simplification output" << std::endl; msg << "\t # vertices \t" << m_nVertices << std::endl; msg << "\t # triangles \t" << m_nTriangles << std::endl; msg << "\t QEM \t" << qem << std::endl; (*m_callBack)(msg.str().c_str(), 100.0, qem, m_nVertices); } return true; } }