1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
|
/**
@file MeshAlgAdjacency.cpp
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2003-09-14
@edited 2010-04-26
Copyright 2000-2010, Morgan McGuire.
All rights reserved.
*/
#include "G3D/Table.h"
#include "G3D/MeshAlg.h"
#include "G3D/Set.h"
#include "G3D/Stopwatch.h"
#include "G3D/SmallArray.h"
#include "G3D/AreaMemoryManager.h"
namespace G3D {
/** Two-level table mapping index 0 -> index 1 -> list of face indices */
class MeshEdgeTable {
public:
/** We expect 2 faces per edge. */
typedef SmallArray<int, 2> FaceIndexArray;
class Edge {
public:
int i1;
FaceIndexArray faceIndexArray;
};
/** We expect at most 6 edges per vertex; that matches a typical regular grid mesh */
typedef SmallArray<Edge, 6> EdgeArray;
typedef Array< EdgeArray > ET;
private:
ET table;
public:
MeshEdgeTable() {
AreaMemoryManager::Ref mm = AreaMemoryManager::create();
table.clearAndSetMemoryManager(mm);
}
void clear() {
table.clear();
}
void resize(int maxV) {
table.resize(maxV);
}
/**
Inserts the faceIndex into the edge's face list.
The index may be a negative number indicating a backface.
\param v0 Vertex index 0
\param v1 Vertex index 1
*/
void insert(int v0, int v1, int faceIndex) {
debugAssert(v0 <= v1);
EdgeArray& edgeArray = table[v0];
for (int i = 0; i < edgeArray.size(); ++i) {
if (edgeArray[i].i1 == v1) {
edgeArray[i].faceIndexArray.push(faceIndex);
return;
}
}
Edge& p = edgeArray.next();
p.i1 = v1;
p.faceIndexArray.push(faceIndex);
}
class Iterator {
friend class MeshEdgeTable;
private:
int m_i0;
/** Pair index */
int m_p;
ET& m_array;
EdgeArray* m_edgeArray;
bool m_end;
public:
int i0() const {
return m_i0;
}
int i1() const {
return (*m_edgeArray)[m_p].i1;
}
FaceIndexArray& faceIndex() {
return (*m_edgeArray)[m_p].faceIndexArray;
}
Iterator& operator++() {
if ((m_i0 >= 0) && (m_p < m_edgeArray->size() - 1)) {
++m_p;
} else {
// Skip over elements with no face array
do {
++m_i0;
if (m_i0 == m_array.size()) {
m_end = true;
return *this;
} else {
m_edgeArray = &m_array[m_i0];
m_p = 0;
}
} while (m_edgeArray->size() == 0);
}
return *this;
}
bool hasMore() const {
return ! m_end;
}
private:
Iterator(ET& a) : m_i0(-1), m_p(-1), m_array(a), m_edgeArray(NULL), m_end(false) {
++(*this);
}
};
Iterator begin() {
return Iterator(table);
}
};
/**
Assigns the edge index into the next unassigned edge
index. The edge index may be negative, indicating
a reverse edge.
*/
static void assignEdgeIndex(MeshAlg::Face& face, int e) {
for (int i = 0; i < 3; ++i) {
if (face.edgeIndex[i] == MeshAlg::Face::NONE) {
face.edgeIndex[i] = e;
return;
}
}
debugAssertM(false, "Face has already been assigned 3 edges");
}
void MeshAlg::computeAdjacency(
const Array<Vector3>& vertexGeometry,
const Array<int>& indexArray,
Array<Face>& faceArray,
Array<Edge>& edgeArray,
Array< Array<int> >& adjacentFaceArray) {
Array<Vertex> vertexArray;
computeAdjacency(vertexGeometry, indexArray, faceArray, edgeArray, vertexArray);
// Convert the vertexArray into adjacentFaceArray
adjacentFaceArray.clear();
adjacentFaceArray.resize(vertexArray.size());
for (int v = 0; v < adjacentFaceArray.size(); ++v) {
const SmallArray<int, 6>& src = vertexArray[v].faceIndex;
Array<int>& dst = adjacentFaceArray[v];
dst.resize(src.size());
for (int f = 0; f < dst.size(); ++f) {
dst[f] = src[f];
}
}
}
void MeshAlg::computeAdjacency(
const Array<Vector3>& vertexGeometry,
const Array<int>& indexArray,
Array<Face>& faceArray,
Array<Edge>& edgeArray,
Array<Vertex>& vertexArray) {
MeshEdgeTable edgeTable;
edgeArray.clear();
vertexArray.clear();
faceArray.clear();
// Face normals
Array<Vector3> faceNormal;
faceNormal.resize(indexArray.size() / 3);
faceArray.resize(faceNormal.size());
// This array has the same size as the vertex array
vertexArray.resize(vertexGeometry.size());
edgeTable.resize(vertexArray.size());
// Iterate through the triangle list
for (int q = 0, f = 0; q < indexArray.size(); ++f, q += 3) {
Vector3 vertex[3];
MeshAlg::Face& face = faceArray[f];
// Construct the face
for (int j = 0; j < 3; ++j) {
int v = indexArray[q + j];
face.vertexIndex[j] = v;
face.edgeIndex[j] = Face::NONE;
// Store back pointers in the vertices
vertexArray[v].faceIndex.append(f);
// We'll need these vertices to find the face normal
vertex[j] = vertexGeometry[v];
}
// Compute the face normal
const Vector3& N = (vertex[1] - vertex[0]).cross(vertex[2] - vertex[0]);
faceNormal[f] = N.directionOrZero();
static const int nextIndex[] = {1, 2, 0};
// Add each edge to the edge table.
for (int j = 0; j < 3; ++j) {
const int i0 = indexArray[q + j];
const int i1 = indexArray[q + nextIndex[j]];
if (i0 < i1) {
// The edge was directed in the same manner as in the face
edgeTable.insert(i0, i1, f);
} else {
// The edge was directed in the opposite manner as in the face
edgeTable.insert(i1, i0, ~f);
}
}
}
// For each edge in the edge table, create an edge in the edge array.
// Collapse every 2 edges from adjacent faces.
MeshEdgeTable::Iterator cur = edgeTable.begin();
Array<Edge> tempEdgeArray;
while (cur.hasMore()) {
MeshEdgeTable::FaceIndexArray& faceIndexArray = cur.faceIndex();
// Process this edge
while (faceIndexArray.size() > 0) {
// Remove the last index
int f0 = faceIndexArray.pop();
// Find the normal to that face
const Vector3& n0 = faceNormal[(f0 >= 0) ? f0 : ~f0];
bool found = false;
// We try to find the matching face with the closest
// normal. This ensures that we don't introduce a lot
// of artificial ridges into flat parts of a mesh.
float ndotn = -2;
int f1 = -1, i1 = -1;
// Try to find the face with the matching edge
for (int i = faceIndexArray.size() - 1; i >= 0; --i) {
int f = faceIndexArray[i];
if ((f >= 0) != (f0 >= 0)) {
// This face contains the oppositely oriented edge
// and has not been assigned too many edges
const Vector3& n1 = faceNormal[(f >= 0) ? f : ~f];
float d = n1.dot(n0);
if (found) {
// We previously found a good face; see if this
// one is better.
if (d > ndotn) {
// This face is better.
ndotn = d;
f1 = f;
i1 = i;
}
} else {
// This is the first face we've found
found = true;
ndotn = d;
f1 = f;
i1 = i;
}
}
}
// Create the new edge
int e = tempEdgeArray.size();
Edge& edge = tempEdgeArray.next();
edge.vertexIndex[0] = cur.i0();
edge.vertexIndex[1] = cur.i1();
if (f0 >= 0) {
edge.faceIndex[0] = f0;
edge.faceIndex[1] = Face::NONE;
assignEdgeIndex(faceArray[f0], e);
} else {
// The face indices above are two's complemented.
// this code restores them to regular indices.
debugAssert((~f0) >= 0);
edge.faceIndex[1] = ~f0;
edge.faceIndex[0] = Face::NONE;
// The edge index *does* need to be inverted, however.
assignEdgeIndex(faceArray[~f0], ~e);
}
if (found) {
// We found a matching face; remove both
// faces from the active list.
faceIndexArray.fastRemove(i1);
if (f1 >= 0) {
edge.faceIndex[0] = f1;
assignEdgeIndex(faceArray[f1], e);
} else {
edge.faceIndex[1] = ~f1;
assignEdgeIndex(faceArray[~f1], ~e);
}
}
}
++cur;
}
edgeTable.clear();
// Move boundary edges to the end of the list and then
// clean up the face references into them
{
// Map old edge indices to new edge indices
Array<int> newIndex;
newIndex.resize(tempEdgeArray.size());
// Index of the start and end of the edge array
int i = 0;
int j = tempEdgeArray.size() - 1;
edgeArray.resize(tempEdgeArray.size());
for (int e = 0; e < tempEdgeArray.size(); ++e) {
if (tempEdgeArray[e].boundary()) {
newIndex[e] = j;
--j;
} else {
newIndex[e] = i;
++i;
}
edgeArray[newIndex[e]] = tempEdgeArray[e];
}
debugAssertM(i == j + 1, "Counting from front and back of array did not match");
// Fix the faces
for (int f = 0; f < faceArray.size(); ++f) {
Face& face = faceArray[f];
for (int q = 0; q < 3; ++q) {
int e = face.edgeIndex[q];
if (e < 0) {
// Backwards edge; twiddle before and after conversion
face.edgeIndex[q] = ~newIndex[~e];
} else {
// Regular edge; remap the index
face.edgeIndex[q] = newIndex[e];
}
}
}
}
// Now order the edge indices inside the faces correctly.
for (int f = 0; f < faceArray.size(); ++f) {
Face& face = faceArray[f];
int e0 = face.edgeIndex[0];
int e1 = face.edgeIndex[1];
int e2 = face.edgeIndex[2];
// e0 will always remain first. The only
// question is whether e1 and e2 should be swapped.
// See if e1 begins at the vertex where e1 ends.
const int e0End = (e0 < 0) ?
edgeArray[~e0].vertexIndex[0] :
edgeArray[e0].vertexIndex[1];
const int e1Begin = (e1 < 0) ?
edgeArray[~e1].vertexIndex[1] :
edgeArray[e1].vertexIndex[0];
if (e0End != e1Begin) {
// We must swap e1 and e2
face.edgeIndex[1] = e2;
face.edgeIndex[2] = e1;
}
}
// Fill out the edge adjacency information in the vertex array
for (int e = 0; e < edgeArray.size(); ++e) {
const Edge& edge = edgeArray[e];
vertexArray[edge.vertexIndex[0]].edgeIndex.append(e);
vertexArray[edge.vertexIndex[1]].edgeIndex.append(~e);
}
}
void MeshAlg::weldBoundaryEdges(
Array<Face>& faceArray,
Array<Edge>& edgeArray,
Array<Vertex>& vertexArray) {
// Copy over the original edge array
Array<Edge> oldEdgeArray = edgeArray;
// newEdgeIndex[e] is the new index of the old edge with index e
// Note that newEdgeIndex[e] might be negative, indicating that
// the edge switched direction between the arrays.
Array<int> newEdgeIndex;
newEdgeIndex.resize(edgeArray.size());
edgeArray.resize(0);
// boundaryEdgeIndices[v_low] is an array of the indices of
// all boundary edges whose lower vertex is v_low.
Table<int, Array<int> > boundaryEdgeIndices;
// Copy over non-boundary edges to the new array
for (int e = 0; e < oldEdgeArray.size(); ++e) {
if (oldEdgeArray[e].boundary()) {
// Add to the boundary table
const int v_low = iMin(oldEdgeArray[e].vertexIndex[0], oldEdgeArray[e].vertexIndex[1]);
if (! boundaryEdgeIndices.containsKey(v_low)) {
boundaryEdgeIndices.set(v_low, Array<int>());
}
boundaryEdgeIndices[v_low].append(e);
// We'll fill out newEdgeIndex[e] later, when we find pairs
} else {
// Copy the edge to the new array
newEdgeIndex[e] = edgeArray.size();
edgeArray.append(oldEdgeArray[e]);
}
}
// Remove all edges from the table that have pairs.
Table<int, Array<int> >::Iterator cur = boundaryEdgeIndices.begin();
Table<int, Array<int> >::Iterator end = boundaryEdgeIndices.end();
while (cur != end) {
Array<int>& boundaryEdge = cur->value;
for (int i = 0; i < boundaryEdge.size(); ++i) {
int ei = boundaryEdge[i];
const Edge& edgei = oldEdgeArray[ei];
for (int j = i + 1; j < boundaryEdge.size(); ++j) {
int ej = boundaryEdge[j];
const Edge& edgej = oldEdgeArray[ej];
// See if edge ei is the reverse (match) of edge ej.
// True if the edges match
bool match = false;
// True if edgej's vertex indices are reversed from
// edgei's (usually true).
bool reversej = false;
int u = edgei.vertexIndex[0];
int v = edgei.vertexIndex[1];
if (edgei.faceIndex[0] != Face::NONE) {
// verts|faces
// edgei = [u v A /]
if (edgej.faceIndex[0] != Face::NONE) {
if ((edgej.vertexIndex[0] == v) && (edgej.vertexIndex[1] == u)) {
// This is the most common of the four cases
// edgej = [v u B /]
match = true;
reversej = true;
}
} else {
if ((edgej.vertexIndex[0] == u) && (edgej.vertexIndex[1] == v)) {
// edgej = [u v / B]
match = true;
}
}
} else {
// edgei = [u v / A]
if (edgej.faceIndex[0] != Face::NONE) {
if ((edgej.vertexIndex[0] == u) && (edgej.vertexIndex[1] == v)) {
// edgej = [u v B /]
match = true;
}
} else {
if ((edgej.vertexIndex[0] == v) && (edgej.vertexIndex[1] == u)) {
// edgej = [v u / B]
match = true;
reversej = true;
}
}
}
if (match) {
// ei and ej can be paired as a single edge
int e = edgeArray.size();
Edge& edge = edgeArray.next();
// Follow the direction of edgei.
edge = edgei;
newEdgeIndex[ei] = e;
// Insert the face index for edgej.
int fj = edgej.faceIndex[0];
if (fj == Face::NONE) {
fj = edgej.faceIndex[1];
}
if (edge.faceIndex[0] == Face::NONE) {
edge.faceIndex[0] = fj;
} else {
edge.faceIndex[1] = fj;
}
if (reversej) {
// The new edge is backwards of the old edge for ej
newEdgeIndex[ej] = ~e;
} else {
newEdgeIndex[ej] = e;
}
// Remove both ei and ej from being candidates for future pairing.
// Remove ej first since it comes later in the list (removing
// ei would decrease the index of ej to j - 1).
boundaryEdge.fastRemove(j);
boundaryEdge.fastRemove(i);
// Re-process element i, which is now a new edge index
--i;
// Jump out of the j for-loop
break;
}
}
}
++cur;
}
// Anything remaining in the table is a real boundary edge; just copy it to
// the end of the array.
cur = boundaryEdgeIndices.begin();
end = boundaryEdgeIndices.end();
while (cur != end) {
Array<int>& boundaryEdge = cur->value;
for (int b = 0; b < boundaryEdge.size(); ++b) {
const int e = boundaryEdge[b];
newEdgeIndex[e] = edgeArray.size();
edgeArray.append(oldEdgeArray[e]);
}
++cur;
}
// Finally, fix up edge indices in the face and vertex arrays
for (int f = 0; f < faceArray.size(); ++f) {
Face& face = faceArray[f];
for (int i = 0; i < 3; ++i) {
int e = face.edgeIndex[i];
if (e < 0) {
face.edgeIndex[i] = ~newEdgeIndex[~e];
} else {
face.edgeIndex[i] = newEdgeIndex[e];
}
}
}
for (int v = 0; v < vertexArray.size(); ++v) {
Vertex& vertex = vertexArray[v];
for (int i = 0; i < vertex.edgeIndex.size(); ++i) {
int e = vertex.edgeIndex[i];
if (e < 0) {
vertex.edgeIndex[i] = ~newEdgeIndex[~e];
} else {
vertex.edgeIndex[i] = newEdgeIndex[e];
}
}
}
}
void MeshAlg::weldAdjacency(
const Array<Vector3>& originalGeometry,
Array<Face>& faceArray,
Array<Edge>& edgeArray,
Array<Vertex>& vertexArray,
double radius) {
// Num vertices
const int n = originalGeometry.size();
// canonical[v] = first occurance of any vertex near oldVertexArray[v]
Array<int> canonical;
canonical.resize(n);
Array<int> toNew, toOld;
// Throw away the new vertex array
Array<Vector3> dummy;
computeWeld(originalGeometry, dummy, toNew, toOld, radius);
for (int v = 0; v < canonical.size(); ++v) {
// Round-trip through the toNew/toOld process. This will give
// us the original vertex.
canonical[v] = toOld[toNew[v]];
}
// Destroy vertexArray (we reconstruct it below)
vertexArray.clear();
vertexArray.resize(n);
bool hasBoundaryEdges = false;
// Fix edge vertex indices
for (int e = 0; e < edgeArray.size(); ++e) {
Edge& edge = edgeArray[e];
const int v0 = canonical[edge.vertexIndex[0]];
const int v1 = canonical[edge.vertexIndex[1]];
edge.vertexIndex[0] = v0;
edge.vertexIndex[1] = v1;
vertexArray[v0].edgeIndex.append(e);
vertexArray[v1].edgeIndex.append(~e);
hasBoundaryEdges = hasBoundaryEdges || edge.boundary();
}
// Fix face vertex indices
for (int f = 0; f < faceArray.size(); ++f) {
Face& face = faceArray[f];
for (int i = 0; i < 3; ++i) {
const int v = canonical[face.vertexIndex[i]];
face.vertexIndex[i] = v;
// Add the back pointer
vertexArray[v].faceIndex.append(f);
}
}
if (hasBoundaryEdges) {
// As a result of the welding, some of the boundary edges at
// the end of the array may now have mates and no longer be
// boundaries. Try to pair these up.
weldBoundaryEdges(faceArray, edgeArray, vertexArray);
}
}
void MeshAlg::debugCheckConsistency(
const Array<Face>& faceArray,
const Array<Edge>& edgeArray,
const Array<Vertex>& vertexArray) {
#ifdef _DEBUG
for (int v = 0; v < vertexArray.size(); ++v) {
const MeshAlg::Vertex& vertex = vertexArray[v];
for (int i = 0; i < vertex.edgeIndex.size(); ++i) {
const int e = vertex.edgeIndex[i];
debugAssert(edgeArray[(e >= 0) ? e : ~e].containsVertex(v));
}
for (int i = 0; i < vertex.faceIndex.size(); ++i) {
const int f = vertex.faceIndex[i];
debugAssert(faceArray[f].containsVertex(v));
}
}
for (int e = 0; e < edgeArray.size(); ++e) {
const MeshAlg::Edge& edge = edgeArray[e];
for (int i = 0; i < 2; ++i) {
debugAssert((edge.faceIndex[i] == MeshAlg::Face::NONE) ||
faceArray[edge.faceIndex[i]].containsEdge(e));
debugAssert(vertexArray[edge.vertexIndex[i]].inEdge(e));
}
}
// Every face's edge must be on that face
for (int f = 0; f < faceArray.size(); ++f) {
const MeshAlg::Face& face = faceArray[f];
for (int i = 0; i < 3; ++i) {
int e = face.edgeIndex[i];
int ei = (e >= 0) ? e : ~e;
debugAssert(edgeArray[ei].inFace(f));
// Make sure the edge is oriented appropriately
if (e >= 0) {
debugAssert(edgeArray[ei].faceIndex[0] == (int)f);
} else {
debugAssert(edgeArray[ei].faceIndex[1] == (int)f);
}
debugAssert(vertexArray[face.vertexIndex[i]].inFace(f));
}
}
#else
(void)faceArray;
(void)edgeArray;
(void)vertexArray;
#endif // _DEBUG
}
} // G3D namespace
|