1285 lines
30 KiB
C++
1285 lines
30 KiB
C++
|
// This code is in the public domain -- castanyo@yahoo.es
|
||
|
|
||
|
#include "nvmesh.h" // pch
|
||
|
|
||
|
#include "Mesh.h"
|
||
|
#include "Edge.h"
|
||
|
#include "Vertex.h"
|
||
|
#include "Face.h"
|
||
|
|
||
|
#include "nvmesh/TriMesh.h"
|
||
|
#include "nvmesh/QuadTriMesh.h"
|
||
|
#include "nvmesh/MeshBuilder.h"
|
||
|
|
||
|
#include "nvmath/Vector.inl"
|
||
|
#include "nvcore/Array.inl"
|
||
|
#include "nvcore/HashMap.inl"
|
||
|
|
||
|
|
||
|
using namespace nv;
|
||
|
using namespace HalfEdge;
|
||
|
|
||
|
Mesh::Mesh() : m_colocalVertexCount(0)
|
||
|
{
|
||
|
errorCount = 0;
|
||
|
}
|
||
|
|
||
|
Mesh::Mesh(const Mesh * mesh)
|
||
|
{
|
||
|
errorCount = 0;
|
||
|
|
||
|
// Copy mesh vertices.
|
||
|
const uint vertexCount = mesh->vertexCount();
|
||
|
m_vertexArray.resize(vertexCount);
|
||
|
|
||
|
for (uint v = 0; v < vertexCount; v++)
|
||
|
{
|
||
|
const Vertex * vertex = mesh->vertexAt(v);
|
||
|
nvDebugCheck(vertex->id == v);
|
||
|
|
||
|
m_vertexArray[v] = new Vertex(v);
|
||
|
m_vertexArray[v]->pos = vertex->pos;
|
||
|
m_vertexArray[v]->nor = vertex->nor;
|
||
|
m_vertexArray[v]->tex = vertex->tex;
|
||
|
}
|
||
|
|
||
|
m_colocalVertexCount = vertexCount;
|
||
|
|
||
|
|
||
|
// Copy mesh faces.
|
||
|
const uint faceCount = mesh->faceCount();
|
||
|
|
||
|
Array<uint> indexArray(3);
|
||
|
|
||
|
for (uint f = 0; f < faceCount; f++)
|
||
|
{
|
||
|
const Face * face = mesh->faceAt(f);
|
||
|
|
||
|
for(Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) {
|
||
|
const Vertex * vertex = it.current()->from();
|
||
|
indexArray.append(vertex->id);
|
||
|
}
|
||
|
|
||
|
addFace(indexArray);
|
||
|
indexArray.clear();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Mesh::~Mesh()
|
||
|
{
|
||
|
clear();
|
||
|
}
|
||
|
|
||
|
|
||
|
void Mesh::clear()
|
||
|
{
|
||
|
deleteAll(m_vertexArray);
|
||
|
m_vertexArray.clear();
|
||
|
|
||
|
foreach(i, m_edgeMap)
|
||
|
{
|
||
|
delete m_edgeMap[i].value;
|
||
|
}
|
||
|
//deleteAll(m_edgeArray); // edgeArray only contains 1/2 of the edges!
|
||
|
m_edgeArray.clear();
|
||
|
m_edgeMap.clear();
|
||
|
|
||
|
deleteAll(m_faceArray);
|
||
|
m_faceArray.clear();
|
||
|
}
|
||
|
|
||
|
|
||
|
Vertex * Mesh::addVertex(const Vector3 & pos)
|
||
|
{
|
||
|
nvDebugCheck(isFinite(pos));
|
||
|
|
||
|
Vertex * v = new Vertex(m_vertexArray.count());
|
||
|
v->pos = pos;
|
||
|
m_vertexArray.append(v);
|
||
|
|
||
|
return v;
|
||
|
|
||
|
// return addVertex(m_vertexArray.count(), pos);
|
||
|
}
|
||
|
|
||
|
/*Vertex * Mesh::addVertex(uint id, const Vector3 & pos)
|
||
|
{
|
||
|
nvDebugCheck(isFinite(pos));
|
||
|
|
||
|
Vertex * v = new Vertex(id);
|
||
|
v->pos = pos;
|
||
|
m_vertexArray.append(v);
|
||
|
|
||
|
return v;
|
||
|
}*/
|
||
|
|
||
|
/*void Mesh::addVertices(const Mesh * mesh)
|
||
|
{
|
||
|
nvCheck(mesh != NULL);
|
||
|
|
||
|
// Add mesh vertices
|
||
|
for (uint v = 0; v < vertexCount; v++)
|
||
|
{
|
||
|
const Vertex * vertex = mesh->vertexAt(v);
|
||
|
nvDebugCheck(vertex != NULL);
|
||
|
|
||
|
Vertex * v = addVertex(vertex->pos());
|
||
|
nvDebugCheck(v != NULL);
|
||
|
|
||
|
v->setNor(vertex->nor());
|
||
|
v->setTex(vertex->tex());
|
||
|
}
|
||
|
}*/
|
||
|
|
||
|
|
||
|
/// Link colocal vertices based on geometric location only.
|
||
|
void Mesh::linkColocals()
|
||
|
{
|
||
|
nvDebug("--- Linking colocals:\n");
|
||
|
|
||
|
const uint vertexCount = this->vertexCount();
|
||
|
HashMap<Vector3, Vertex *> vertexMap(vertexCount);
|
||
|
|
||
|
for (uint v = 0; v < vertexCount; v++)
|
||
|
{
|
||
|
Vertex * vertex = vertexAt(v);
|
||
|
|
||
|
Vertex * colocal;
|
||
|
if (vertexMap.get(vertex->pos, &colocal))
|
||
|
{
|
||
|
colocal->linkColocal(vertex);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
vertexMap.add(vertex->pos, vertex);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_colocalVertexCount = vertexMap.count();
|
||
|
|
||
|
nvDebug("--- %d vertex positions.\n", m_colocalVertexCount);
|
||
|
|
||
|
// @@ Remove duplicated vertices? or just leave them as colocals?
|
||
|
}
|
||
|
|
||
|
void Mesh::linkColocalsWithCanonicalMap(const Array<uint> & canonicalMap)
|
||
|
{
|
||
|
nvDebug("--- Linking colocals:\n");
|
||
|
|
||
|
uint vertexMapSize = 0;
|
||
|
foreach(i, canonicalMap) {
|
||
|
vertexMapSize = max(vertexMapSize, canonicalMap[i] + 1);
|
||
|
}
|
||
|
|
||
|
Array<Vertex *> vertexMap;
|
||
|
vertexMap.resize(vertexMapSize, NULL);
|
||
|
|
||
|
m_colocalVertexCount = 0;
|
||
|
|
||
|
const uint vertexCount = this->vertexCount();
|
||
|
for (uint v = 0; v < vertexCount; v++)
|
||
|
{
|
||
|
Vertex * vertex = vertexAt(v);
|
||
|
|
||
|
Vertex * colocal = vertexMap[canonicalMap[v]];
|
||
|
if (colocal != NULL)
|
||
|
{
|
||
|
nvDebugCheck(vertex->pos == colocal->pos);
|
||
|
colocal->linkColocal(vertex);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
vertexMap[canonicalMap[v]] = vertex;
|
||
|
m_colocalVertexCount++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
nvDebug("--- %d vertex positions.\n", m_colocalVertexCount);
|
||
|
}
|
||
|
|
||
|
|
||
|
Face * Mesh::addFace()
|
||
|
{
|
||
|
Face * f = new Face(m_faceArray.count());
|
||
|
m_faceArray.append(f);
|
||
|
return f;
|
||
|
}
|
||
|
|
||
|
Face * Mesh::addFace(uint v0, uint v1, uint v2)
|
||
|
{
|
||
|
Array<uint> indexArray(3);
|
||
|
indexArray << v0 << v1 << v2;
|
||
|
return addFace(indexArray, 0, 3);
|
||
|
}
|
||
|
|
||
|
Face * Mesh::addFace(uint v0, uint v1, uint v2, uint v3)
|
||
|
{
|
||
|
Array<uint> indexArray(4);
|
||
|
indexArray << v0 << v1 << v2 << v3;
|
||
|
return addFace(indexArray, 0, 4);
|
||
|
}
|
||
|
|
||
|
Face * Mesh::addFace(const Array<uint> & indexArray)
|
||
|
{
|
||
|
return addFace(indexArray, 0, indexArray.count());
|
||
|
}
|
||
|
|
||
|
|
||
|
Face * Mesh::addFace(const Array<uint> & indexArray, uint first, uint num)
|
||
|
{
|
||
|
nvDebugCheck(first < indexArray.count());
|
||
|
nvDebugCheck(num <= indexArray.count()-first);
|
||
|
nvDebugCheck(num > 2);
|
||
|
|
||
|
if (!canAddFace(indexArray, first, num)) {
|
||
|
errorCount++;
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
Face * f = new Face(m_faceArray.count());
|
||
|
|
||
|
Edge * firstEdge = NULL;
|
||
|
Edge * last = NULL;
|
||
|
Edge * current = NULL;
|
||
|
|
||
|
for(uint i = 0; i < num-1; i++)
|
||
|
{
|
||
|
current = addEdge(indexArray[first+i], indexArray[first+i+1]);
|
||
|
nvCheck(current != NULL && current->face == NULL);
|
||
|
|
||
|
current->face = f;
|
||
|
|
||
|
if (last != NULL) last->setNext(current);
|
||
|
else firstEdge = current;
|
||
|
|
||
|
last = current;
|
||
|
}
|
||
|
|
||
|
current = addEdge(indexArray[first+num-1], indexArray[first]);
|
||
|
nvCheck(current != NULL && current->face == NULL);
|
||
|
|
||
|
current->face = f;
|
||
|
|
||
|
last->setNext(current);
|
||
|
current->setNext(firstEdge);
|
||
|
|
||
|
f->edge = firstEdge;
|
||
|
m_faceArray.append(f);
|
||
|
|
||
|
return f;
|
||
|
}
|
||
|
|
||
|
/*void Mesh::addFaces(const Mesh * mesh)
|
||
|
{
|
||
|
nvCheck(mesh != NULL);
|
||
|
|
||
|
Array indexArray;
|
||
|
// Add faces
|
||
|
|
||
|
}*/
|
||
|
|
||
|
|
||
|
// Return true if the face can be added to the manifold mesh.
|
||
|
bool Mesh::canAddFace(const Array<uint> & indexArray, uint first, uint num) const
|
||
|
{
|
||
|
for (uint j = num - 1, i = 0; i < num; j = i++) {
|
||
|
if (!canAddEdge(indexArray[first+j], indexArray[first+i])) {
|
||
|
errorIndex0 = indexArray[first+j];
|
||
|
errorIndex1 = indexArray[first+i];
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// We also have to make sure the face does not have any duplicate edge!
|
||
|
for (uint i = 0; i < num; i++) {
|
||
|
|
||
|
int i0 = indexArray[first + i + 0];
|
||
|
int i1 = indexArray[first + (i + 1)%num];
|
||
|
|
||
|
for (uint j = i + 1; j < num; j++) {
|
||
|
int j0 = indexArray[first + j + 0];
|
||
|
int j1 = indexArray[first + (j + 1)%num];
|
||
|
|
||
|
if (i0 == j0 && i1 == j1) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Return true if the edge doesn't exist or doesn't have any adjacent face.
|
||
|
bool Mesh::canAddEdge(uint i, uint j) const
|
||
|
{
|
||
|
if (i == j) {
|
||
|
// Skip degenerate edges.
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Same check, but taking into account colocal vertices.
|
||
|
const Vertex * v0 = vertexAt(i);
|
||
|
const Vertex * v1 = vertexAt(j);
|
||
|
|
||
|
for(Vertex::ConstVertexIterator it(v0->colocals()); !it.isDone(); it.advance())
|
||
|
{
|
||
|
if (it.current() == v1)
|
||
|
{
|
||
|
// Skip degenerate edges.
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Make sure edge has not been added yet.
|
||
|
Edge * edge = findEdge(i, j);
|
||
|
|
||
|
return edge == NULL || edge->face == NULL; // We ignore edges that don't have an adjacent face yet, since this face could become the edge's face.
|
||
|
}
|
||
|
|
||
|
Edge * Mesh::addEdge(uint i, uint j)
|
||
|
{
|
||
|
nvCheck(i != j);
|
||
|
|
||
|
Edge * edge = findEdge(i, j);
|
||
|
|
||
|
if (edge != NULL) {
|
||
|
// Edge may already exist, but its face must not be set.
|
||
|
nvDebugCheck(edge->face == NULL);
|
||
|
|
||
|
// Nothing else to do!
|
||
|
|
||
|
}
|
||
|
else {
|
||
|
// Add new edge.
|
||
|
|
||
|
// Lookup pair.
|
||
|
Edge * pair = findEdge(j, i);
|
||
|
|
||
|
if (pair != NULL)
|
||
|
{
|
||
|
// Create edge with same id.
|
||
|
edge = new Edge(pair->id + 1);
|
||
|
|
||
|
// Link edge pairs.
|
||
|
edge->pair = pair;
|
||
|
pair->pair = edge;
|
||
|
|
||
|
// @@ I'm not sure this is necessary!
|
||
|
pair->vertex->setEdge(pair);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Create edge.
|
||
|
edge = new Edge(2*m_edgeArray.count());
|
||
|
|
||
|
// Add only unpaired edges.
|
||
|
m_edgeArray.append(edge);
|
||
|
}
|
||
|
|
||
|
edge->vertex = m_vertexArray[i];
|
||
|
m_edgeMap.add(Key(i,j), edge);
|
||
|
}
|
||
|
|
||
|
// Face and Next are set by addFace.
|
||
|
|
||
|
return edge;
|
||
|
}
|
||
|
|
||
|
|
||
|
/// Find edge, test all colocals.
|
||
|
Edge * Mesh::findEdge(uint i, uint j) const
|
||
|
{
|
||
|
Edge * edge = NULL;
|
||
|
|
||
|
const Vertex * v0 = vertexAt(i);
|
||
|
const Vertex * v1 = vertexAt(j);
|
||
|
|
||
|
// Test all colocal pairs.
|
||
|
for(Vertex::ConstVertexIterator it0(v0->colocals()); !it0.isDone(); it0.advance())
|
||
|
{
|
||
|
for(Vertex::ConstVertexIterator it1(v1->colocals()); !it1.isDone(); it1.advance())
|
||
|
{
|
||
|
Key key(it0.current()->id, it1.current()->id);
|
||
|
|
||
|
if (edge == NULL) {
|
||
|
m_edgeMap.get(key, &edge);
|
||
|
#if !defined(_DEBUG)
|
||
|
if (edge != NULL) return edge;
|
||
|
#endif
|
||
|
}
|
||
|
else {
|
||
|
// Make sure that only one edge is found.
|
||
|
nvDebugCheck(!m_edgeMap.get(key));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return edge;
|
||
|
}
|
||
|
|
||
|
/// Link boundary edges once the mesh has been created.
|
||
|
void Mesh::linkBoundary()
|
||
|
{
|
||
|
nvDebug("--- Linking boundaries:\n");
|
||
|
|
||
|
int num = 0;
|
||
|
|
||
|
// Create boundary edges.
|
||
|
uint edgeCount = this->edgeCount();
|
||
|
for(uint e = 0; e < edgeCount; e++)
|
||
|
{
|
||
|
Edge * edge = edgeAt(e);
|
||
|
if (edge != NULL && edge->pair == NULL) {
|
||
|
Edge * pair = new Edge(edge->id + 1);
|
||
|
|
||
|
uint i = edge->from()->id;
|
||
|
uint j = edge->next->from()->id;
|
||
|
|
||
|
Key key(j,i);
|
||
|
nvCheck(!m_edgeMap.get(key));
|
||
|
|
||
|
pair->vertex = m_vertexArray[j];
|
||
|
m_edgeMap.add(key, pair);
|
||
|
|
||
|
edge->pair = pair;
|
||
|
pair->pair = edge;
|
||
|
|
||
|
num++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Link boundary edges.
|
||
|
for (uint e = 0; e < edgeCount; e++) {
|
||
|
Edge * edge = edgeAt(e);
|
||
|
if (edge != NULL && edge->pair->face == NULL) {
|
||
|
linkBoundaryEdge(edge->pair);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
nvDebug("--- %d boundary edges.\n", num);
|
||
|
}
|
||
|
|
||
|
/// Link this boundary edge.
|
||
|
void Mesh::linkBoundaryEdge(Edge * edge)
|
||
|
{
|
||
|
nvCheck(edge->face == NULL);
|
||
|
|
||
|
// Make sure next pointer has not been set. @@ We want to be able to relink boundary edges after mesh changes.
|
||
|
//nvCheck(edge->next() == NULL);
|
||
|
|
||
|
Edge * next = edge;
|
||
|
while(next->pair->face != NULL) {
|
||
|
// Get pair prev
|
||
|
Edge * e = next->pair->next;
|
||
|
while (e->next != next->pair) {
|
||
|
e = e->next;
|
||
|
}
|
||
|
next = e;
|
||
|
}
|
||
|
edge->setNext(next->pair);
|
||
|
|
||
|
// Adjust vertex edge, so that it's the boundary edge. (required for isBoundary())
|
||
|
if (edge->vertex->edge != edge)
|
||
|
{
|
||
|
// Multiple boundaries in the same edge.
|
||
|
//nvCheck( edge->vertex()->edge() == NULL || edge->vertex()->edge()->face() != NULL );
|
||
|
edge->vertex->edge = edge;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/// Convert to tri mesh.
|
||
|
TriMesh * Mesh::toTriMesh() const
|
||
|
{
|
||
|
uint triangleCount = 0;
|
||
|
|
||
|
// Count triangle faces.
|
||
|
const uint faceCount = this->faceCount();
|
||
|
for(uint f = 0; f < faceCount; f++)
|
||
|
{
|
||
|
const Face * face = faceAt(f);
|
||
|
triangleCount += face->edgeCount() - 2;
|
||
|
}
|
||
|
|
||
|
TriMesh * triMesh = new TriMesh(triangleCount, vertexCount());
|
||
|
|
||
|
// Add vertices.
|
||
|
Array<TriMesh::Vertex> & vertices = triMesh->vertices();
|
||
|
|
||
|
const uint vertexCount = this->vertexCount();
|
||
|
for(uint v = 0; v < vertexCount; v++)
|
||
|
{
|
||
|
const Vertex * vertex = vertexAt(v);
|
||
|
|
||
|
TriMesh::Vertex triVertex;
|
||
|
triVertex.id = vertices.count();
|
||
|
triVertex.pos = vertex->pos;
|
||
|
triVertex.nor = vertex->nor;
|
||
|
triVertex.tex = vertex->tex;
|
||
|
|
||
|
vertices.append(triVertex);
|
||
|
}
|
||
|
|
||
|
// Add triangles.
|
||
|
Array<TriMesh::Face> & triangles = triMesh->faces();
|
||
|
|
||
|
for(uint f = 0; f < faceCount; f++)
|
||
|
{
|
||
|
const Face * face = faceAt(f);
|
||
|
|
||
|
// @@ Triangulate arbitrary polygons correctly.
|
||
|
const uint v0 = face->edge->vertex->id;
|
||
|
uint v1 = face->edge->next->vertex->id;
|
||
|
|
||
|
for(Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance())
|
||
|
{
|
||
|
uint v2 = it.current()->vertex->id;
|
||
|
|
||
|
// Skip the first two vertices.
|
||
|
if (v2 == v0 || v2 == v1) continue;
|
||
|
|
||
|
TriMesh::Face triangle;
|
||
|
triangle.id = triangles.count();
|
||
|
triangle.v[0] = v0;
|
||
|
triangle.v[1] = v1;
|
||
|
triangle.v[2] = v2;
|
||
|
|
||
|
v1 = v2;
|
||
|
|
||
|
triangles.append(triangle);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return triMesh;
|
||
|
}
|
||
|
|
||
|
QuadTriMesh * Mesh::toQuadTriMesh() const
|
||
|
{
|
||
|
MeshBuilder builder;
|
||
|
|
||
|
const uint vertexCount = this->vertexCount();
|
||
|
builder.hintVertexCount(vertexCount);
|
||
|
|
||
|
for(uint v = 0; v < vertexCount; v++)
|
||
|
{
|
||
|
const Vertex * vertex = vertexAt(v);
|
||
|
|
||
|
builder.addPosition(vertex->pos);
|
||
|
builder.addNormal(vertex->nor);
|
||
|
builder.addTexCoord(vertex->tex);
|
||
|
}
|
||
|
|
||
|
const uint faceCount = this->faceCount();
|
||
|
builder.hintTriangleCount(faceCount);
|
||
|
|
||
|
for(uint f = 0; f < faceCount; f++)
|
||
|
{
|
||
|
const Face * face = faceAt(f);
|
||
|
|
||
|
builder.beginPolygon();
|
||
|
|
||
|
for(Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance())
|
||
|
{
|
||
|
uint v = it.current()->vertex->id;
|
||
|
builder.addVertex(v, v, v);
|
||
|
}
|
||
|
|
||
|
builder.endPolygon();
|
||
|
}
|
||
|
|
||
|
builder.done();
|
||
|
|
||
|
return builder.buildQuadTriMesh();
|
||
|
}
|
||
|
|
||
|
|
||
|
// Triangulate in place.
|
||
|
void Mesh::triangulate() {
|
||
|
|
||
|
bool all_triangles = true;
|
||
|
|
||
|
const uint faceCount = m_faceArray.count();
|
||
|
for (uint f = 0; f < faceCount; f++) {
|
||
|
Face * face = m_faceArray[f];
|
||
|
if (face->edgeCount() != 3) {
|
||
|
all_triangles = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (all_triangles) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Do not touch vertices, but rebuild edges and faces.
|
||
|
Array<Edge *> edgeArray;
|
||
|
Array<Face *> faceArray;
|
||
|
|
||
|
swap(edgeArray, m_edgeArray);
|
||
|
swap(faceArray, m_faceArray);
|
||
|
m_edgeMap.clear();
|
||
|
|
||
|
for (uint f = 0; f < faceCount; f++) {
|
||
|
Face * face = faceArray[f];
|
||
|
|
||
|
// Trivial fan-like triangulation.
|
||
|
const uint v0 = face->edge->vertex->id;
|
||
|
uint v2, v1 = -1;
|
||
|
|
||
|
for (Face::EdgeIterator it(face->edges()); !it.isDone(); it.advance()) {
|
||
|
Edge * edge = it.current();
|
||
|
v2 = edge->to()->id;
|
||
|
if (v2 == v0) break;
|
||
|
if (v1 != -1) addFace(v0, v1, v2);
|
||
|
v1 = v2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
nvDebugCheck(m_faceArray.count() > faceCount); // triangle count > face count
|
||
|
|
||
|
linkBoundary();
|
||
|
|
||
|
deleteAll(edgeArray);
|
||
|
deleteAll(faceArray);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
Fixing T-junctions.
|
||
|
|
||
|
- Find T-junctions. Find vertices that are on an edge.
|
||
|
- This test is approximate.
|
||
|
- Insert edges on a spatial index to speedup queries.
|
||
|
- Consider only open edges, that is edges that have no pairs.
|
||
|
- Consider only vertices on boundaries.
|
||
|
- Close T-junction.
|
||
|
- Split edge.
|
||
|
|
||
|
*/
|
||
|
bool Mesh::splitBoundaryEdges() {
|
||
|
|
||
|
Array<Vertex *> boundaryVertices;
|
||
|
|
||
|
foreach(i, m_vertexArray) {
|
||
|
Vertex * v = m_vertexArray[i];
|
||
|
if (v->isBoundary()) {
|
||
|
boundaryVertices.append(v);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
nvDebug("Fixing T-junctions:\n");
|
||
|
|
||
|
int splitCount = 0;
|
||
|
|
||
|
foreach(v, boundaryVertices) {
|
||
|
Vertex * vertex = boundaryVertices[v];
|
||
|
|
||
|
Vector3 x0 = vertex->pos;
|
||
|
|
||
|
// Find edges that this vertex overlaps with.
|
||
|
foreach(e, m_edgeArray) {
|
||
|
//for (uint e = 0; e < m_edgeArray.count(); e++) {
|
||
|
Edge * edge = m_edgeArray[e];
|
||
|
if (edge != NULL && edge->isBoundary()) {
|
||
|
|
||
|
if (edge->from() == vertex || edge->to() == vertex) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
Vector3 x1 = edge->from()->pos;
|
||
|
Vector3 x2 = edge->to()->pos;
|
||
|
|
||
|
Vector3 v01 = x0 - x1;
|
||
|
Vector3 v21 = x2 - x1;
|
||
|
|
||
|
float l = length(v21);
|
||
|
float d = length(cross(v01, v21)) / l;
|
||
|
|
||
|
if (isZero(d)) {
|
||
|
float t = dot(v01, v21) / (l * l);
|
||
|
|
||
|
// @@ Snap x0 to x1 or x2, if too close? No, do vertex snapping elsewhere.
|
||
|
/*if (equal(t, 0.0f, 0.01f)) {
|
||
|
//vertex->setPos(x1);
|
||
|
}
|
||
|
else if (equal(t, 1.0f, 0.01f)) {
|
||
|
//vertex->setPos(x2);
|
||
|
}
|
||
|
else*/
|
||
|
if (t > 0.0f + NV_EPSILON && t < 1.0f - NV_EPSILON) {
|
||
|
nvDebugCheck(equal(lerp(x1, x2, t), x0));
|
||
|
|
||
|
Vertex * splitVertex = splitBoundaryEdge(edge, t, x0);
|
||
|
vertex->linkColocal(splitVertex); // @@ Should we do this here?
|
||
|
splitCount++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
nvDebug(" - %d edges split.\n", splitCount);
|
||
|
|
||
|
nvDebugCheck(isValid());
|
||
|
|
||
|
return splitCount != 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
// For this to be effective, we have to fix the boundary junctions first.
|
||
|
Edge * Mesh::sewBoundary(Edge * startEdge) {
|
||
|
nvDebugCheck(startEdge->face == NULL);
|
||
|
|
||
|
// @@ We may want to be more conservative linking colocals in order to preserve the input topology. One way of doing that is by linking colocals only
|
||
|
// if the vertices next to them are linked as well. That is, by sewing boundaries after detecting them. If any pair of consecutive edges have their first
|
||
|
// and last vertex in the same position, then it can be linked.
|
||
|
|
||
|
Edge * lastBoundarySeen = startEdge;
|
||
|
|
||
|
nvDebug("Sewing Boundary:\n");
|
||
|
|
||
|
int count = 0;
|
||
|
int sewnCount = 0;
|
||
|
|
||
|
Edge * edge = startEdge;
|
||
|
do {
|
||
|
nvDebugCheck(edge->face == NULL);
|
||
|
|
||
|
Edge * edge_a = edge;
|
||
|
Edge * edge_b = edge->prev;
|
||
|
|
||
|
Edge * pair_a = edge_a->pair;
|
||
|
Edge * pair_b = edge_b->pair;
|
||
|
|
||
|
Vertex * v0a = edge_a->to();
|
||
|
Vertex * v0b = edge_b->from();
|
||
|
Vertex * v1a = edge_a->from();
|
||
|
Vertex * v1b = edge_b->to();
|
||
|
|
||
|
nvDebugCheck(v1a->isColocal(v1b));
|
||
|
|
||
|
/*
|
||
|
v0b + _+ v0a
|
||
|
\ /
|
||
|
b \ / a
|
||
|
\|/
|
||
|
v1b + v1a
|
||
|
*/
|
||
|
|
||
|
// @@ This should not happen while sewing, but it may be produced somewhere else.
|
||
|
nvDebugCheck(edge_a != edge_b);
|
||
|
|
||
|
if (v0a->pos == v0b->pos) {
|
||
|
|
||
|
// Link vertices.
|
||
|
v0a->linkColocal(v0b);
|
||
|
|
||
|
// Remove edges to be collapsed.
|
||
|
disconnect(edge_a);
|
||
|
disconnect(edge_b);
|
||
|
disconnect(pair_a);
|
||
|
disconnect(pair_b);
|
||
|
|
||
|
// Link new boundary edges.
|
||
|
Edge * prevBoundary = edge_b->prev;
|
||
|
Edge * nextBoundary = edge_a->next;
|
||
|
if (nextBoundary != NULL) {
|
||
|
nvDebugCheck(nextBoundary->face == NULL);
|
||
|
nvDebugCheck(prevBoundary->face == NULL);
|
||
|
nextBoundary->setPrev(prevBoundary);
|
||
|
|
||
|
// Make sure boundary vertex points to boundary edge.
|
||
|
v0a->setEdge(nextBoundary); // This updates all colocals.
|
||
|
}
|
||
|
lastBoundarySeen = prevBoundary;
|
||
|
|
||
|
// Creat new edge.
|
||
|
Edge * newEdge_a = addEdge(v0a->id, v1a->id); // pair_a->from()->id, pair_a->to()->id
|
||
|
Edge * newEdge_b = addEdge(v1b->id, v0b->id);
|
||
|
|
||
|
newEdge_a->pair = newEdge_b;
|
||
|
newEdge_b->pair = newEdge_a;
|
||
|
|
||
|
newEdge_a->face = pair_a->face;
|
||
|
newEdge_b->face = pair_b->face;
|
||
|
|
||
|
newEdge_a->setNext(pair_a->next);
|
||
|
newEdge_a->setPrev(pair_a->prev);
|
||
|
|
||
|
newEdge_b->setNext(pair_b->next);
|
||
|
newEdge_b->setPrev(pair_b->prev);
|
||
|
|
||
|
delete edge_a;
|
||
|
delete edge_b;
|
||
|
delete pair_a;
|
||
|
delete pair_b;
|
||
|
|
||
|
edge = nextBoundary; // If nextBoundary is NULL we have closed the loop.
|
||
|
sewnCount++;
|
||
|
}
|
||
|
else {
|
||
|
edge = edge->next;
|
||
|
}
|
||
|
|
||
|
count++;
|
||
|
} while(edge != NULL && edge != lastBoundarySeen);
|
||
|
|
||
|
nvDebug(" - Sewn %d out of %d.\n", sewnCount, count);
|
||
|
|
||
|
if (lastBoundarySeen != NULL) {
|
||
|
nvDebugCheck(lastBoundarySeen->face == NULL);
|
||
|
}
|
||
|
|
||
|
return lastBoundarySeen;
|
||
|
}
|
||
|
|
||
|
|
||
|
// @@ We must always disconnect edge pairs simultaneously.
|
||
|
void Mesh::disconnect(Edge * edge) {
|
||
|
nvDebugCheck(edge != NULL);
|
||
|
|
||
|
// Remove from edge list.
|
||
|
if ((edge->id & 1) == 0) {
|
||
|
nvDebugCheck(m_edgeArray[edge->id / 2] == edge);
|
||
|
m_edgeArray[edge->id / 2] = NULL;
|
||
|
}
|
||
|
|
||
|
// Remove edge from map. @@ Store map key inside edge?
|
||
|
nvDebugCheck(edge->from() != NULL && edge->to() != NULL);
|
||
|
bool removed = m_edgeMap.remove(Key(edge->from()->id, edge->to()->id));
|
||
|
nvDebugCheck(removed == true);
|
||
|
|
||
|
// Disconnect from vertex.
|
||
|
if (edge->vertex != NULL) {
|
||
|
if (edge->vertex->edge == edge) {
|
||
|
if (edge->prev && edge->prev->pair) {
|
||
|
edge->vertex->edge = edge->prev->pair;
|
||
|
}
|
||
|
else if (edge->pair && edge->pair->next) {
|
||
|
edge->vertex->edge = edge->pair->next;
|
||
|
}
|
||
|
else {
|
||
|
edge->vertex->edge = NULL;
|
||
|
// @@ Remove disconnected vertex?
|
||
|
}
|
||
|
}
|
||
|
//edge->setVertex(NULL);
|
||
|
}
|
||
|
|
||
|
// Disconnect from face.
|
||
|
if (edge->face != NULL) {
|
||
|
if (edge->face->edge == edge) {
|
||
|
if (edge->next != NULL && edge->next != edge) {
|
||
|
edge->face->edge = edge->next;
|
||
|
}
|
||
|
else if (edge->prev != NULL && edge->prev != edge) {
|
||
|
edge->face->edge = edge->prev;
|
||
|
}
|
||
|
else {
|
||
|
edge->face->edge = NULL;
|
||
|
// @@ Remove disconnected face?
|
||
|
}
|
||
|
}
|
||
|
//edge->setFace(NULL);
|
||
|
}
|
||
|
|
||
|
// @@ Hack, we don't disconnect from pair, because pair needs us to remove itself from the map.
|
||
|
// Disconect from pair.
|
||
|
/*if (edge->pair != NULL) {
|
||
|
if (edge->pair->pair == edge) {
|
||
|
edge->pair->setPair(NULL);
|
||
|
}
|
||
|
//edge->setPair(NULL);
|
||
|
}*/
|
||
|
|
||
|
// Disconnect from previous.
|
||
|
if (edge->prev) {
|
||
|
if (edge->prev->next == edge) {
|
||
|
edge->prev->setNext(NULL);
|
||
|
}
|
||
|
//edge->setPrev(NULL);
|
||
|
}
|
||
|
|
||
|
// Disconnect from next.
|
||
|
if (edge->next) {
|
||
|
if (edge->next->prev == edge) {
|
||
|
edge->next->setPrev(NULL);
|
||
|
}
|
||
|
//edge->setNext(NULL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void Mesh::remove(Edge * edge) {
|
||
|
nvDebugCheck(edge != NULL);
|
||
|
|
||
|
disconnect(edge);
|
||
|
|
||
|
delete edge;
|
||
|
}
|
||
|
|
||
|
void Mesh::remove(Vertex * vertex) {
|
||
|
nvDebugCheck(vertex != NULL);
|
||
|
|
||
|
// Remove from vertex list.
|
||
|
m_vertexArray[vertex->id] = NULL;
|
||
|
|
||
|
// Disconnect from colocals.
|
||
|
vertex->unlinkColocal();
|
||
|
|
||
|
// Disconnect from edges.
|
||
|
if (vertex->edge != NULL) {
|
||
|
// @@ Removing a connected vertex is asking for trouble...
|
||
|
if (vertex->edge->vertex == vertex) {
|
||
|
// @@ Connect edge to a colocal?
|
||
|
vertex->edge->vertex = NULL;
|
||
|
}
|
||
|
|
||
|
vertex->setEdge(NULL);
|
||
|
}
|
||
|
|
||
|
delete vertex;
|
||
|
}
|
||
|
|
||
|
void Mesh::remove(Face * face) {
|
||
|
nvDebugCheck(face != NULL);
|
||
|
|
||
|
// Remove from face list.
|
||
|
m_faceArray[face->id] = NULL;
|
||
|
|
||
|
// Disconnect from edges.
|
||
|
if (face->edge != NULL) {
|
||
|
nvDebugCheck(face->edge->face == face);
|
||
|
|
||
|
face->edge->face = NULL;
|
||
|
|
||
|
face->edge = NULL;
|
||
|
}
|
||
|
|
||
|
delete face;
|
||
|
}
|
||
|
|
||
|
|
||
|
void Mesh::compactEdges() {
|
||
|
const uint edgeCount = m_edgeArray.count();
|
||
|
|
||
|
uint c = 0;
|
||
|
for (uint i = 0; i < edgeCount; i++) {
|
||
|
if (m_edgeArray[i] != NULL) {
|
||
|
if (i != c) {
|
||
|
m_edgeArray[c] = m_edgeArray[i];
|
||
|
m_edgeArray[c]->id = 2 * c;
|
||
|
if (m_edgeArray[c]->pair != NULL) {
|
||
|
m_edgeArray[c]->pair->id = 2 * c + 1;
|
||
|
}
|
||
|
}
|
||
|
c++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_edgeArray.resize(c);
|
||
|
}
|
||
|
|
||
|
|
||
|
void Mesh::compactVertices() {
|
||
|
const uint vertexCount = m_vertexArray.count();
|
||
|
|
||
|
uint c = 0;
|
||
|
for (uint i = 0; i < vertexCount; i++) {
|
||
|
if (m_vertexArray[i] != NULL) {
|
||
|
if (i != c) {
|
||
|
m_vertexArray[c] = m_vertexArray[i];
|
||
|
m_vertexArray[c]->id = c;
|
||
|
}
|
||
|
c++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_vertexArray.resize(c);
|
||
|
|
||
|
// @@ Generate xref array for external attributes.
|
||
|
}
|
||
|
|
||
|
|
||
|
void Mesh::compactFaces() {
|
||
|
const uint faceCount = m_faceArray.count();
|
||
|
|
||
|
uint c = 0;
|
||
|
for (uint i = 0; i < faceCount; i++) {
|
||
|
if (m_faceArray[i] != NULL) {
|
||
|
if (i != c) {
|
||
|
m_faceArray[c] = m_faceArray[i];
|
||
|
m_faceArray[c]->id = c;
|
||
|
}
|
||
|
c++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_faceArray.resize(c);
|
||
|
}
|
||
|
|
||
|
|
||
|
Vertex * Mesh::splitBoundaryEdge(Edge * edge, float t, const Vector3 & pos) {
|
||
|
|
||
|
/*
|
||
|
We want to go from this configuration:
|
||
|
|
||
|
+ +
|
||
|
| ^
|
||
|
edge |<->| pair
|
||
|
v |
|
||
|
+ +
|
||
|
|
||
|
To this one:
|
||
|
|
||
|
+ +
|
||
|
| ^
|
||
|
e0 |<->| p0
|
||
|
v |
|
||
|
vertex + +
|
||
|
| ^
|
||
|
e1 |<->| p1
|
||
|
v |
|
||
|
+ +
|
||
|
|
||
|
*/
|
||
|
|
||
|
|
||
|
Edge * pair = edge->pair;
|
||
|
|
||
|
// Make sure boundaries are linked.
|
||
|
nvDebugCheck(pair != NULL);
|
||
|
|
||
|
// Make sure edge is a boundary edge.
|
||
|
nvDebugCheck(pair->face == NULL);
|
||
|
|
||
|
// Add new vertex.
|
||
|
Vertex * vertex = addVertex(pos);
|
||
|
vertex->nor = lerp(edge->from()->nor, edge->to()->nor, t);
|
||
|
vertex->tex = lerp(edge->from()->tex, edge->to()->tex, t);
|
||
|
vertex->col = lerp(edge->from()->col, edge->to()->col, t);
|
||
|
|
||
|
disconnect(edge);
|
||
|
disconnect(pair);
|
||
|
|
||
|
// Add edges.
|
||
|
Edge * e0 = addEdge(edge->from()->id, vertex->id);
|
||
|
Edge * p0 = addEdge(vertex->id, pair->to()->id);
|
||
|
|
||
|
Edge * e1 = addEdge(vertex->id, edge->to()->id);
|
||
|
Edge * p1 = addEdge(pair->from()->id, vertex->id);
|
||
|
|
||
|
// Link edges.
|
||
|
e0->setNext(e1);
|
||
|
p1->setNext(p0);
|
||
|
|
||
|
e0->setPrev(edge->prev);
|
||
|
e1->setNext(edge->next);
|
||
|
|
||
|
p1->setPrev(pair->prev);
|
||
|
p0->setNext(pair->next);
|
||
|
|
||
|
nvDebugCheck(e0->next == e1);
|
||
|
nvDebugCheck(e1->prev == e0);
|
||
|
|
||
|
nvDebugCheck(p1->next == p0);
|
||
|
nvDebugCheck(p0->prev == p1);
|
||
|
|
||
|
nvDebugCheck(p0->pair == e0);
|
||
|
nvDebugCheck(e0->pair == p0);
|
||
|
|
||
|
nvDebugCheck(p1->pair == e1);
|
||
|
nvDebugCheck(e1->pair == p1);
|
||
|
|
||
|
// Link faces.
|
||
|
e0->face = edge->face;
|
||
|
e1->face = edge->face;
|
||
|
|
||
|
// Link vertices.
|
||
|
edge->from()->setEdge(e0);
|
||
|
vertex->setEdge(e1);
|
||
|
|
||
|
delete edge;
|
||
|
delete pair;
|
||
|
|
||
|
return vertex;
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
// Without introducing new vertices.
|
||
|
void Mesh::splitBoundaryEdge(Edge * edge, Vertex * vertex) {
|
||
|
|
||
|
/*
|
||
|
We want to go from this configuration:
|
||
|
|
||
|
| | pn
|
||
|
+ +
|
||
|
| ^
|
||
|
| |
|
||
|
edge |<->| pair
|
||
|
| |
|
||
|
v |
|
||
|
+ +
|
||
|
| | pp
|
||
|
|
||
|
To this one:
|
||
|
\ /
|
||
|
\ /
|
||
|
+ +
|
||
|
| ^
|
||
|
e0 |<->| p0
|
||
|
v |
|
||
|
vertex + +
|
||
|
| ^
|
||
|
e1 |<->| p1
|
||
|
v |
|
||
|
+ +
|
||
|
/ \
|
||
|
/ \
|
||
|
*/
|
||
|
|
||
|
|
||
|
Edge * pair = edge->pair;
|
||
|
Edge * pn = pair->next();
|
||
|
Edge * pp = pair->prev();
|
||
|
|
||
|
// Make sure boundaries are linked.
|
||
|
nvDebugCheck(pair != NULL);
|
||
|
|
||
|
// Make sure edge is a boundary edge.
|
||
|
nvDebugCheck(pair->face() == NULL);
|
||
|
|
||
|
nvDebugCheck(edge->isValid());
|
||
|
nvDebugCheck(pair->isValid());
|
||
|
|
||
|
disconnect(edge);
|
||
|
disconnect(pair);
|
||
|
|
||
|
// Add edges.
|
||
|
Edge * e0 = addEdge(edge->from()->id(), vertex->id());
|
||
|
Edge * e1 = addEdge(vertex->id(), edge->to()->id());
|
||
|
|
||
|
// Link faces.
|
||
|
e0->setFace(edge->face());
|
||
|
e1->setFace(edge->face());
|
||
|
|
||
|
// Link pairs.
|
||
|
Edge * p0 = findEdge(vertex->id(), pair->to()->id());
|
||
|
if (p0 == NULL) {
|
||
|
p0 = addEdge(vertex->id(), pair->to()->id());
|
||
|
pn->setPrev(p0);
|
||
|
}
|
||
|
else {
|
||
|
nvDebugCheck(p0->face() != NULL);
|
||
|
if (e0->prev() != NULL) {
|
||
|
pn->setPrev(e0->prev());
|
||
|
}
|
||
|
else {
|
||
|
nvDebugCheck(pn == e0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Edge * p1 = findEdge(pair->from()->id(), vertex->id());
|
||
|
if (p1 == NULL) {
|
||
|
p1 = addEdge(pair->from()->id(), vertex->id());
|
||
|
pp->setNext(p1);
|
||
|
}
|
||
|
else {
|
||
|
nvDebugCheck(p1->face() != NULL);
|
||
|
if (e1->next() != NULL) {
|
||
|
pp->setPrev(e1->next());
|
||
|
}
|
||
|
else {
|
||
|
nvDebugCheck(pp == e1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Link edges.
|
||
|
e0->setNext(e1); // e1->setPrev(e0)
|
||
|
|
||
|
if (p0->face() == p1->face()) { // can be null
|
||
|
p1->setNext(p0); // p0->setPrev(p1)
|
||
|
}
|
||
|
else {
|
||
|
//if (p1->face() == NULL) p1->setNext(
|
||
|
}
|
||
|
|
||
|
|
||
|
e0->setPrev(edge->prev());
|
||
|
e1->setNext(edge->next());
|
||
|
|
||
|
nvDebugCheck(e0->pair == p0);
|
||
|
nvDebugCheck(e1->pair == p1);
|
||
|
nvDebugCheck(p0->pair == e0);
|
||
|
nvDebugCheck(p1->pair == e1);
|
||
|
|
||
|
nvDebugCheck(e0->isValid());
|
||
|
nvDebugCheck(e1->isValid());
|
||
|
nvDebugCheck(pp->isValid());
|
||
|
nvDebugCheck(pn->isValid());
|
||
|
|
||
|
nvDebugCheck(e0->pair->isValid());
|
||
|
nvDebugCheck(e1->pair->isValid());
|
||
|
nvDebugCheck(pp->pair->isValid());
|
||
|
nvDebugCheck(pn->pair->isValid());
|
||
|
|
||
|
nvDebugCheck(edge->face->isValid());
|
||
|
|
||
|
if (pn->pair->face != NULL) {
|
||
|
nvDebugCheck(pn->pair->face->isValid());
|
||
|
}
|
||
|
|
||
|
if (pp->pair->face() != NULL) {
|
||
|
nvDebugCheck(pn->pair->face->isValid());
|
||
|
}
|
||
|
|
||
|
if (p0->face != NULL) {
|
||
|
nvDebugCheck(p0->face->isValid());
|
||
|
}
|
||
|
|
||
|
if (p1->face() != NULL) {
|
||
|
nvDebugCheck(p1->face()->isValid());
|
||
|
}
|
||
|
|
||
|
nvDebugCheck(isValid()); // Only for extreme debugging.
|
||
|
|
||
|
// Link vertices.
|
||
|
edge->from()->setEdge(e0);
|
||
|
vertex->setEdge(p0);
|
||
|
|
||
|
delete edge;
|
||
|
delete pair;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
bool Mesh::isValid() const
|
||
|
{
|
||
|
// Make sure all edges are valid.
|
||
|
const uint edgeCount = m_edgeArray.count();
|
||
|
for (uint e = 0; e < edgeCount; e++) {
|
||
|
Edge * edge = m_edgeArray[e];
|
||
|
if (edge != NULL) {
|
||
|
if (edge->id != 2*e) {
|
||
|
return false;
|
||
|
}
|
||
|
if (!edge->isValid()) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (edge->pair->id != 2*e+1) {
|
||
|
return false;
|
||
|
}
|
||
|
if (!edge->pair->isValid()) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// @@ Make sure all faces are valid.
|
||
|
|
||
|
// @@ Make sure all vertices are valid.
|
||
|
|
||
|
return true;
|
||
|
}
|