TrinityCore
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
G3D::MeshAlg Class Reference

#include <MeshAlg.h>

Classes

class  Edge
 
class  Face
 
class  Geometry
 
class  Vertex
 

Public Types

typedef PrimitiveType Primitive
 

Static Public Member Functions

static void computeAdjacency (const Array< Vector3 > &vertexGeometry, const Array< int > &indexArray, Array< Face > &faceArray, Array< Edge > &edgeArray, Array< Vertex > &vertexArray)
 
static void computeAdjacency (const Array< Vector3 > &vertexArray, const Array< int > &indexArray, Array< Face > &faceArray, Array< Edge > &edgeArray, Array< Array< int > > &facesAdjacentToVertex)
 
static void computeAreaStatistics (const Array< Vector3 > &vertexArray, const Array< int > &indexArray, double &minEdgeLength, double &meanEdgeLength, double &medianEdgeLength, double &maxEdgeLength, double &minFaceArea, double &meanFaceArea, double &medianFaceArea, double &maxFaceArea)
 
static void computeTangentSpaceBasis (const Array< Vector3 > &vertexArray, const Array< Vector2 > &texCoordArray, const Array< Vector3 > &vertexNormalArray, const Array< Face > &faceArray, Array< Vector3 > &tangent, Array< Vector3 > &binormal)
 
static void computeNormals (const Array< Vector3 > &vertexArray, const Array< Face > &faceArray, const Array< Array< int > > &adjacentFaceArray, Array< Vector3 > &vertexNormalArray, Array< Vector3 > &faceNormalArray)
 
static void computeNormals (const Array< Vector3 > &vertexGeometry, const Array< Face > &faceArray, const Array< Vertex > &vertexArray, Array< Vector3 > &vertexNormalArray, Array< Vector3 > &faceNormalArray)
 
static void computeNormals (Geometry &geometry, const Array< int > &indexArray)
 
static void computeFaceNormals (const Array< Vector3 > &vertexArray, const Array< Face > &faceArray, Array< Vector3 > &faceNormals, bool normalize=true)
 
static void identifyBackfaces (const Array< Vector3 > &vertexArray, const Array< Face > &faceArray, const Vector4 &P, Array< bool > &backface)
 
static void identifyBackfaces (const Array< Vector3 > &vertexArray, const Array< Face > &faceArray, const Vector4 &P, Array< bool > &backface, const Array< Vector3 > &faceNormals)
 
static void computeWeld (const Array< Vector3 > &oldVertexPositions, Array< Vector3 > &newVertexPositions, Array< int > &toNew, Array< int > &toOld, float radius=fuzzyEpsilon32)
 
static void weldAdjacency (const Array< Vector3 > &originalGeometry, Array< Face > &faceArray, Array< Edge > &edgeArray, Array< Vertex > &vertexArray, float radius=fuzzyEpsilon32)
 
static int countBoundaryEdges (const Array< Edge > &edgeArray)
 
static void createIndexArray (int n, Array< int > &array, int start=0, int run=1, int skip=0)
 
static void computeBounds (const Array< Vector3 > &vertex, class AABox &box, class Sphere &sphere)
 
static void computeBounds (const Array< Vector3 > &vertex, const Array< int > &index, class AABox &box, class Sphere &sphere)
 
static void debugCheckConsistency (const Array< Face > &faceArray, const Array< Edge > &edgeArray, const Array< Vertex > &vertexArray)
 
static void generateGrid (Array< Vector3 > &vertex, Array< Vector2 > &texCoord, Array< int > &index, int wCells=10, int hCells=10, const Vector2 &textureScale=Vector2(1, 1), bool spaceCentered=true, bool twoSided=true, const CoordinateFrame &xform=CoordinateFrame(), const Image1::Ref &elevation=Image1::Ref())
 
template<class IndexType >
static void toIndexedTriList (const Array< IndexType > &inIndices, MeshAlg::Primitive inType, Array< IndexType > &outIndices)
 

Static Protected Member Functions

static int findEdgeIndex (const Array< Vector3 > &vertexArray, Array< Edge > &geometricEdgeArray, int i0, int i1, int f, double area)
 

Static Private Member Functions

static void weldBoundaryEdges (Array< Face > &faceArray, Array< Edge > &edgeArray, Array< Vertex > &vertexArray)
 

Detailed Description

Indexed mesh algorithms. You have to build your own mesh class.

No mesh class is provided with G3D because there isn't an "ideal" mesh format– one application needs keyframed animation, another skeletal animation, a third texture coordinates, a fourth cannot precompute information, etc. Instead of compromising, this class implements the hard parts of mesh computation and you can write your own ideal mesh class on top of it.

See also
G3D::ArticulatedModel, G3D::IFSModel

Member Typedef Documentation

Member Function Documentation

void G3D::MeshAlg::computeAdjacency ( const Array< Vector3 > &  vertexGeometry,
const Array< int > &  indexArray,
Array< Face > &  faceArray,
Array< Edge > &  edgeArray,
Array< Vertex > &  vertexArray 
)
static

Given a set of vertices and a set of indices for traversing them to create triangles, computes other mesh properties.

Colocated vertices are treated as separate. To have colocated vertices collapsed (necessary for many algorithms, like shadowing), weld the mesh before computing adjacency.

Recent change: In version 6.00, colocated vertices were automatically welded by this routine and degenerate faces and edges were removed. That is no longer the case.

Where two faces meet, there are two opposite directed edges. These are collapsed into a single bidirectional edge in the edgeArray. If four faces meet exactly at the same edge, that edge will appear twice in the array, and so on. If an edge is a boundary of the mesh (i.e. if the edge has only one adjacent face) it will appear in the array with one face index set to MeshAlg::Face::NONE.

Parameters
vertexGeometryVertex positions to use when deciding colocation.
indexArrayOrder to traverse vertices to make triangles
faceArrayOutput
edgeArrayOutput. Sorted so that boundary edges are at the end of the array.
vertexArrayOutput
198  {
199 
200  MeshEdgeTable edgeTable;
201 
202  edgeArray.clear();
203  vertexArray.clear();
204  faceArray.clear();
205 
206  // Face normals
207  Array<Vector3> faceNormal;
208  faceNormal.resize(indexArray.size() / 3);
209  faceArray.resize(faceNormal.size());
210 
211  // This array has the same size as the vertex array
212  vertexArray.resize(vertexGeometry.size());
213 
214  edgeTable.resize(vertexArray.size());
215 
216  // Iterate through the triangle list
217  for (int q = 0, f = 0; q < indexArray.size(); ++f, q += 3) {
218 
219  Vector3 vertex[3];
220  MeshAlg::Face& face = faceArray[f];
221 
222  // Construct the face
223  for (int j = 0; j < 3; ++j) {
224  int v = indexArray[q + j];
225  face.vertexIndex[j] = v;
226  face.edgeIndex[j] = Face::NONE;
227 
228  // Store back pointers in the vertices
229  vertexArray[v].faceIndex.append(f);
230 
231  // We'll need these vertices to find the face normal
232  vertex[j] = vertexGeometry[v];
233  }
234 
235  // Compute the face normal
236  const Vector3& N = (vertex[1] - vertex[0]).cross(vertex[2] - vertex[0]);
237  faceNormal[f] = N.directionOrZero();
238 
239  static const int nextIndex[] = {1, 2, 0};
240 
241  // Add each edge to the edge table.
242  for (int j = 0; j < 3; ++j) {
243  const int i0 = indexArray[q + j];
244  const int i1 = indexArray[q + nextIndex[j]];
245 
246  if (i0 < i1) {
247  // The edge was directed in the same manner as in the face
248  edgeTable.insert(i0, i1, f);
249  } else {
250  // The edge was directed in the opposite manner as in the face
251  edgeTable.insert(i1, i0, ~f);
252  }
253  }
254  }
255 
256  // For each edge in the edge table, create an edge in the edge array.
257  // Collapse every 2 edges from adjacent faces.
258 
259  MeshEdgeTable::Iterator cur = edgeTable.begin();
260 
261  Array<Edge> tempEdgeArray;
262  while (cur.isValid()) {
263  MeshEdgeTable::FaceIndexArray& faceIndexArray = cur.faceIndex();
264 
265  // Process this edge
266  while (faceIndexArray.size() > 0) {
267 
268  // Remove the last index
269  int f0 = faceIndexArray.pop();
270 
271  // Find the normal to that face
272  const Vector3& n0 = faceNormal[(f0 >= 0) ? f0 : ~f0];
273 
274  bool found = false;
275 
276  // We try to find the matching face with the closest
277  // normal. This ensures that we don't introduce a lot
278  // of artificial ridges into flat parts of a mesh.
279  float ndotn = -2;
280  int f1 = -1, i1 = -1;
281 
282  // Try to find the face with the matching edge
283  for (int i = faceIndexArray.size() - 1; i >= 0; --i) {
284  int f = faceIndexArray[i];
285 
286  if ((f >= 0) != (f0 >= 0)) {
287  // This face contains the oppositely oriented edge
288  // and has not been assigned too many edges
289 
290  const Vector3& n1 = faceNormal[(f >= 0) ? f : ~f];
291  float d = n1.dot(n0);
292 
293  if (found) {
294  // We previously found a good face; see if this
295  // one is better.
296  if (d > ndotn) {
297  // This face is better.
298  ndotn = d;
299  f1 = f;
300  i1 = i;
301  }
302  } else {
303  // This is the first face we've found
304  found = true;
305  ndotn = d;
306  f1 = f;
307  i1 = i;
308  }
309  }
310  }
311 
312  // Create the new edge
313  int e = tempEdgeArray.size();
314  Edge& edge = tempEdgeArray.next();
315 
316  edge.vertexIndex[0] = cur.i0();
317  edge.vertexIndex[1] = cur.i1();
318 
319  if (f0 >= 0) {
320  edge.faceIndex[0] = f0;
321  edge.faceIndex[1] = Face::NONE;
322  assignEdgeIndex(faceArray[f0], e);
323  } else {
324  // The face indices above are two's complemented.
325  // this code restores them to regular indices.
326  debugAssert((~f0) >= 0);
327  edge.faceIndex[1] = ~f0;
328  edge.faceIndex[0] = Face::NONE;
329 
330  // The edge index *does* need to be inverted, however.
331  assignEdgeIndex(faceArray[~f0], ~e);
332  }
333 
334  if (found) {
335  // We found a matching face; remove both
336  // faces from the active list.
337  faceIndexArray.fastRemove(i1);
338 
339  if (f1 >= 0) {
340  edge.faceIndex[0] = f1;
341  assignEdgeIndex(faceArray[f1], e);
342  } else {
343  edge.faceIndex[1] = ~f1;
344  assignEdgeIndex(faceArray[~f1], ~e);
345  }
346  }
347  }
348 
349  ++cur;
350  }
351 
352  edgeTable.clear();
353 
354  // Move boundary edges to the end of the list and then
355  // clean up the face references into them
356  {
357  // Map old edge indices to new edge indices
358  Array<int> newIndex;
359  newIndex.resize(tempEdgeArray.size());
360 
361  // Index of the start and end of the edge array
362  int i = 0;
363  int j = tempEdgeArray.size() - 1;
364 
365  edgeArray.resize(tempEdgeArray.size());
366  for (int e = 0; e < tempEdgeArray.size(); ++e) {
367  if (tempEdgeArray[e].boundary()) {
368  newIndex[e] = j;
369  --j;
370  } else {
371  newIndex[e] = i;
372  ++i;
373  }
374  edgeArray[newIndex[e]] = tempEdgeArray[e];
375  }
376 
377  debugAssertM(i == j + 1, "Counting from front and back of array did not match");
378 
379  // Fix the faces
380  for (int f = 0; f < faceArray.size(); ++f) {
381  Face& face = faceArray[f];
382  for (int q = 0; q < 3; ++q) {
383  int e = face.edgeIndex[q];
384  if (e < 0) {
385  // Backwards edge; twiddle before and after conversion
386  face.edgeIndex[q] = ~newIndex[~e];
387  } else {
388  // Regular edge; remap the index
389  face.edgeIndex[q] = newIndex[e];
390  }
391  }
392  }
393  }
394 
395  // Now order the edge indices inside the faces correctly.
396  for (int f = 0; f < faceArray.size(); ++f) {
397  Face& face = faceArray[f];
398  int e0 = face.edgeIndex[0];
399  int e1 = face.edgeIndex[1];
400  int e2 = face.edgeIndex[2];
401 
402  // e0 will always remain first. The only
403  // question is whether e1 and e2 should be swapped.
404 
405  // See if e1 begins at the vertex where e1 ends.
406  const int e0End = (e0 < 0) ?
407  edgeArray[~e0].vertexIndex[0] :
408  edgeArray[e0].vertexIndex[1];
409 
410  const int e1Begin = (e1 < 0) ?
411  edgeArray[~e1].vertexIndex[1] :
412  edgeArray[e1].vertexIndex[0];
413 
414  if (e0End != e1Begin) {
415  // We must swap e1 and e2
416  face.edgeIndex[1] = e2;
417  face.edgeIndex[2] = e1;
418  }
419  }
420 
421  // Fill out the edge adjacency information in the vertex array
422  for (int e = 0; e < edgeArray.size(); ++e) {
423  const Edge& edge = edgeArray[e];
424  vertexArray[edge.vertexIndex[0]].edgeIndex.append(e);
425  vertexArray[edge.vertexIndex[1]].edgeIndex.append(~e);
426  }
427 }
void insert(int n, const T &value)
Definition: Array.h:456
Vector3 directionOrZero() const
Definition: Vector3.h:237
static void assignEdgeIndex(MeshAlg::Face &face, int e)
Definition: MeshAlgAdjacency.cpp:156
#define debugAssertM(exp, message)
Definition: debugAssert.h:161
static const int NONE
Definition: MeshAlg.h:103
#define debugAssert(exp)
Definition: debugAssert.h:160
T pop()
Definition: SmallArray.h:132
SmallArray< int, 2 > FaceIndexArray
Definition: MeshAlgAdjacency.cpp:27
int size() const
Definition: Array.h:430
Vector3 cross(const Vector3 &v1, const Vector3 &v2)
Definition: vectorMath.h:144

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

void G3D::MeshAlg::computeAdjacency ( const Array< Vector3 > &  vertexArray,
const Array< int > &  indexArray,
Array< Face > &  faceArray,
Array< Edge > &  edgeArray,
Array< Array< int > > &  facesAdjacentToVertex 
)
static
Deprecated:
Use the other version of computeAdjacency, which takes Array<Vertex>.
Parameters
facesAdjacentToVertexOutput adjacentFaceArray[v] is an array of indices for faces touching vertex index v
173  {
174 
175  Array<Vertex> vertexArray;
176 
177  computeAdjacency(vertexGeometry, indexArray, faceArray, edgeArray, vertexArray);
178 
179  // Convert the vertexArray into adjacentFaceArray
180  adjacentFaceArray.clear();
181  adjacentFaceArray.resize(vertexArray.size());
182  for (int v = 0; v < adjacentFaceArray.size(); ++v) {
183  const SmallArray<int, 6>& src = vertexArray[v].faceIndex;
184  Array<int>& dst = adjacentFaceArray[v];
185  dst.resize(src.size());
186  for (int f = 0; f < dst.size(); ++f) {
187  dst[f] = src[f];
188  }
189  }
190 }
static void computeAdjacency(const Array< Vector3 > &vertexGeometry, const Array< int > &indexArray, Array< Face > &faceArray, Array< Edge > &edgeArray, Array< Vertex > &vertexArray)
Definition: MeshAlgAdjacency.cpp:193

+ Here is the call graph for this function:

void G3D::MeshAlg::computeAreaStatistics ( const Array< Vector3 > &  vertexArray,
const Array< int > &  indexArray,
double &  minEdgeLength,
double &  meanEdgeLength,
double &  medianEdgeLength,
double &  maxEdgeLength,
double &  minFaceArea,
double &  meanFaceArea,
double &  medianFaceArea,
double &  maxFaceArea 
)
static

Computes some basic mesh statistics including: min, max mean and median, edge lengths; and min, mean, median, and max face area.

Parameters
vertexArrayVertex positions to use when deciding colocation.
indexArrayOrder to traverse vertices to make triangles
minEdgeLengthMinimum edge length
meanEdgeLengthMean edge length
medianEdgeLengthMedian edge length
maxEdgeLengthMax edge length
minFaceAreaMinimum face area
meanFaceAreaMean face area
medianFaceAreaMedian face area
maxFaceAreaMax face area
353  {
354 
355  debugAssert(indexArray.size() % 3 == 0);
356 
357  Array<double> area;
358  area.resize(indexArray.size() / 3);
359  Array<double> magnitude;
360  magnitude.resize(indexArray.size());
361 
362  for (int i = 0; i < indexArray.size(); i += 3) {
363  const Vector3& v0 = vertexArray[indexArray[i]];
364  const Vector3& v1 = vertexArray[indexArray[i + 1]];
365  const Vector3& v2 = vertexArray[indexArray[i + 2]];
366 
367  area[i / 3] = (v1 - v0).cross(v2 - v0).magnitude() / 2.0;
368  magnitude[i] = (v1 - v0).magnitude();
369  magnitude[i + 1] = (v2 - v1).magnitude();
370  magnitude[i + 2] = (v0 - v2).magnitude();
371  }
372 
373  area.sort();
374  magnitude.sort();
375 
376  minEdgeLength = max(0.0, magnitude[0]);
377  maxEdgeLength = max(0.0, magnitude.last());
378  medianEdgeLength = max(0.0, magnitude[magnitude.size() / 2]);
379  meanEdgeLength = 0;
380  for (int i = 0; i < magnitude.size(); ++i) {
381  meanEdgeLength += magnitude[i];
382  }
383  meanEdgeLength /= magnitude.size();
384 
385  minFaceArea = max(0.0, area[0]);
386  maxFaceArea = max(0.0, area.last());
387  medianFaceArea = max(0.0, area[area.size() / 2]);
388  meanFaceArea = 0;
389  for (int i = 0; i < area.size(); ++i) {
390  meanFaceArea += area[i];
391  }
392  meanFaceArea /= area.size();
393 
394 
395  // Make sure round-off hasn't pushed values less than zero
396  meanFaceArea = max(0.0, meanFaceArea);
397  meanEdgeLength = max(0.0, meanEdgeLength);
398 }
T max(const T &x, const T &y)
Definition: g3dmath.h:320
#define debugAssert(exp)
Definition: debugAssert.h:160
float magnitude() const
Definition: Vector3.h:746
int size() const
Definition: Array.h:430
Vector3 cross(const Vector3 &v1, const Vector3 &v2)
Definition: vectorMath.h:144

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

void G3D::MeshAlg::computeBounds ( const Array< Vector3 > &  vertex,
class AABox box,
class Sphere sphere 
)
static

Computes a conservative, near-optimal axis aligned bounding box and sphere.

[The] bounding sphere uses the method from J. Ritter. An effcient bounding sphere. In Andrew S. Glassner, editor, Graphics Gems. Academic Press, Boston, MA, 1990.

433  {
434 
435  Vector3 xmin, xmax, ymin, ymax, zmin, zmax;
436 
437  // FIRST PASS: find 6 minima/maxima points
438  xmin.x = ymin.y = zmin.z = finf();
439  xmax.x = ymax.y = zmax.z = -finf();
440 
441  for (int v = 0; v < vertexArray.size(); ++v) {
442  const Vector3& vertex = vertexArray[v];
443 
444  if (vertex.x < xmin.x) {
445  xmin = vertex;
446  }
447 
448  if (vertex.x > xmax.x) {
449  xmax = vertex;
450  }
451 
452  if (vertex.y < ymin.y) {
453  ymin = vertex;
454  }
455 
456  if (vertex.y > ymax.y) {
457  ymax = vertex;
458  }
459 
460  if (vertex.z < zmin.z) {
461  zmin = vertex;
462  }
463 
464  if (vertex.z > zmax.z) {
465  zmax = vertex;
466  }
467  }
468 
469  // Set points dia1 & dia2 to the maximally separated pair
470  Vector3 dia1 = xmin;
471  Vector3 dia2 = xmax;
472  {
473  // Set xspan = distance between the 2 points xmin & xmax (squared)
474  float xspan = (xmax - xmin).squaredMagnitude();
475 
476  // Same for y & z spans
477  float yspan = (ymax - ymin).squaredMagnitude();
478  float zspan = (zmax - zmin).squaredMagnitude();
479 
480  float maxspan = xspan;
481 
482  if (yspan > maxspan) {
483  maxspan = yspan;
484  dia1 = ymin;
485  dia2 = ymax;
486  }
487 
488  if (zspan > maxspan) {
489  maxspan = zspan;
490  dia1 = zmin;
491  dia2 = zmax;
492  }
493  }
494 
495 
496  // dia1, dia2 is a diameter of initial sphere
497 
498  // calc initial center
499  Vector3 center = (dia1 + dia2) / 2.0;
500 
501  // calculate initial radius^2 and radius
502  Vector3 d = dia2 - sphere.center;
503 
504  float radSq = d.squaredMagnitude();
505  float rad = sqrt(radSq);
506 
507  // SECOND PASS: increment current sphere
508  float old_to_p, old_to_new;
509 
510  for (int v = 0; v < vertexArray.size(); ++v) {
511  const Vector3& vertex = vertexArray[v];
512 
513  d = vertex - center;
514 
515  float old_to_p_sq = d.squaredMagnitude();
516 
517  // do r^2 test first
518  if (old_to_p_sq > radSq) {
519  // this point is outside of current sphere
520  old_to_p = sqrt(old_to_p_sq);
521 
522  // calc radius of new sphere
523  rad = (rad + old_to_p) / 2.0f;
524 
525  // for next r^2 compare
526  radSq = rad * rad;
527  old_to_new = old_to_p - rad;
528 
529  // calc center of new sphere
530  center = (rad * center + old_to_new * vertex) / old_to_p;
531  }
532  }
533 
534  const Vector3 min(xmin.x, ymin.y, zmin.z);
535  const Vector3 max(xmax.x, ymax.y, zmax.z);
536 
537  box = AABox(min, max);
538 
539  const float boxRadSq = (max - min).squaredMagnitude() * 0.25f;
540 
541  if (boxRadSq >= radSq){
542  if (isNaN(center.x) || ! isFinite(rad)) {
543  sphere = Sphere(Vector3::zero(), finf());
544  } else {
545  sphere = Sphere(center, rad);
546  }
547  } else {
548  sphere = Sphere((max + min) * 0.5f, sqrt(boxRadSq));
549  }
550 }
float finf()
Definition: g3dmath.cpp:71
bool isNaN(double x)
Definition: g3dmath.cpp:56
T max(const T &x, const T &y)
Definition: g3dmath.h:320
static const Vector3 & zero()
Definition: Vector3.cpp:119
T min(const T &x, const T &y)
Definition: g3dmath.h:305
bool isFinite(double x)
Definition: g3dmath.h:525

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

void G3D::MeshAlg::computeBounds ( const Array< Vector3 > &  vertex,
const Array< int > &  index,
class AABox box,
class Sphere sphere 
)
static

Computes bounds for a subset of the vertices. It is ok if vertices appear more than once in the index array.

418  {
419 
420  // Makes a copy so as to re-use the existing computebounds code
421  Array<Vector3> newArray;
422  newArray.resize(indexArray.size());
423  for (int i = 0; i < indexArray.size(); ++i) {
424  newArray[i] = vertexArray[indexArray[i]];
425  }
426  computeBounds(newArray, box, sphere);
427 }
static void computeBounds(const Array< Vector3 > &vertex, class AABox &box, class Sphere &sphere)
Definition: MeshAlg.cpp:430

+ Here is the call graph for this function:

void G3D::MeshAlg::computeFaceNormals ( const Array< Vector3 > &  vertexArray,
const Array< Face > &  faceArray,
Array< Vector3 > &  faceNormals,
bool  normalize = true 
)
static

Computes face normals only. Significantly faster (especially if normalize is false) than computeNormals.

See also
weld
224  {
225 
226  faceNormals.resize(faceArray.size());
227 
228  for (int f = 0; f < faceArray.size(); ++f) {
229  const MeshAlg::Face& face = faceArray[f];
230 
231  const Vector3& v0 = vertexArray[face.vertexIndex[0]];
232  const Vector3& v1 = vertexArray[face.vertexIndex[1]];
233  const Vector3& v2 = vertexArray[face.vertexIndex[2]];
234 
235  faceNormals[f] = (v1 - v0).cross(v2 - v0);
236  }
237 
238  if (normalize) {
239  for (int f = 0; f < faceArray.size(); ++f) {
240  faceNormals[f] = faceNormals[f].direction();
241  }
242  }
243 }
Vector3 direction() const
Definition: Vector3.h:756
float normalize(float v)
Definition: g3dmath.h:438
Vector3 cross(const Vector3 &v1, const Vector3 &v2)
Definition: vectorMath.h:144

+ Here is the call graph for this function:

void G3D::MeshAlg::computeNormals ( const Array< Vector3 > &  vertexArray,
const Array< Face > &  faceArray,
const Array< Array< int > > &  adjacentFaceArray,
Array< Vector3 > &  vertexNormalArray,
Array< Vector3 > &  faceNormalArray 
)
static
Deprecated:
149  {
150 
151  // Construct a fake vertex array for backwards compatibility
152  Array<Vertex> fakeVertexArray;
153  fakeVertexArray.resize(adjacentFaceArray.size());
154 
155  for (int v = 0; v < adjacentFaceArray.size(); ++v) {
156  fakeVertexArray[v].faceIndex.resize(adjacentFaceArray[v].size());
157  for (int i = 0; i < fakeVertexArray[v].faceIndex.size(); ++i) {
158  fakeVertexArray[v].faceIndex[i] = adjacentFaceArray[v][i];
159  }
160  // We leave out the edges because they aren't used to compute normals
161  }
162 
163  computeNormals(vertexGeometry, faceArray, fakeVertexArray,
164  vertexNormalArray, faceNormalArray);
165 }
static void computeNormals(const Array< Vector3 > &vertexArray, const Array< Face > &faceArray, const Array< Array< int > > &adjacentFaceArray, Array< Vector3 > &vertexNormalArray, Array< Vector3 > &faceNormalArray)
Definition: MeshAlg.cpp:144

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

void G3D::MeshAlg::computeNormals ( const Array< Vector3 > &  vertexGeometry,
const Array< Face > &  faceArray,
const Array< Vertex > &  vertexArray,
Array< Vector3 > &  vertexNormalArray,
Array< Vector3 > &  faceNormalArray 
)
static

Vertex normals are weighted by the area of adjacent faces. Nelson Max showed this is superior to uniform weighting for general meshes in jgt.

Parameters
vertexNormalArrayOutput. Unit length
faceNormalArrayOutput. Degenerate faces produce zero magnitude normals. Unit length
See also
weld
173  {
174 
175  // Face normals (not unit length)
176  faceNormalArray.resize(faceArray.size());
177  for (int f = 0; f < faceArray.size(); ++f) {
178  const Face& face = faceArray[f];
179 
180  Vector3 vertex[3];
181  for (int j = 0; j < 3; ++j) {
182  vertex[j] = vertexGeometry[face.vertexIndex[j]];
183  debugAssert(vertex[j].isFinite());
184  }
185 
186  faceNormalArray[f] = (vertex[1] - vertex[0]).cross(vertex[2] - vertex[0]);
187 # ifdef G3D_DEBUG
188  const Vector3& N = faceNormalArray[f];
189  debugAssert(N.isFinite());
190 # endif
191  }
192 
193  // Per-vertex normals, computed by averaging
194  vertexNormalArray.resize(vertexGeometry.size());
195  for (int v = 0; v < vertexNormalArray.size(); ++v) {
196  Vector3 sum = Vector3::zero();
197  for (int k = 0; k < vertexArray[v].faceIndex.size(); ++k) {
198  const int f = vertexArray[v].faceIndex[k];
199  sum += faceNormalArray[f];
200  }
201  vertexNormalArray[v] = sum.directionOrZero();
202 # ifdef G3D_DEBUG
203  const Vector3& N = vertexNormalArray[v];
204  debugAssert(N.isUnit() || N.isZero());
205 # endif
206  }
207 
208 
209  for (int f = 0; f < faceArray.size(); ++f) {
210  faceNormalArray[f] = faceNormalArray[f].directionOrZero();
211 # ifdef G3D_DEBUG
212  const Vector3& N = faceNormalArray[f];
213  debugAssert(N.isUnit() || N.isZero());
214 # endif
215  }
216 
217 }
static const Vector3 & zero()
Definition: Vector3.cpp:119
#define debugAssert(exp)
Definition: debugAssert.h:160
Vector3 cross(const Vector3 &v1, const Vector3 &v2)
Definition: vectorMath.h:144
bool isFinite(double x)
Definition: g3dmath.h:525

+ Here is the call graph for this function:

void G3D::MeshAlg::computeNormals ( Geometry geometry,
const Array< int > &  indexArray 
)
static

Computes unit length normals in place using the other computeNormals methods. If you already have a face array use another method; it will be faster.

See also
weld
130  {
131 
132  Array<Face> faceArray;
133  Array<Vertex> vertexArray;
134  Array<Edge> edgeArray;
135  Array<Vector3> faceNormalArray;
136 
137  computeAdjacency(geometry.vertexArray, indexArray, faceArray, edgeArray, vertexArray);
138 
139  computeNormals(geometry.vertexArray, faceArray, vertexArray,
140  geometry.normalArray, faceNormalArray);
141 }
static void computeAdjacency(const Array< Vector3 > &vertexGeometry, const Array< int > &indexArray, Array< Face > &faceArray, Array< Edge > &edgeArray, Array< Vertex > &vertexArray)
Definition: MeshAlgAdjacency.cpp:193
static void computeNormals(const Array< Vector3 > &vertexArray, const Array< Face > &faceArray, const Array< Array< int > > &adjacentFaceArray, Array< Vector3 > &vertexNormalArray, Array< Vector3 > &faceNormalArray)
Definition: MeshAlg.cpp:144

+ Here is the call graph for this function:

void G3D::MeshAlg::computeTangentSpaceBasis ( const Array< Vector3 > &  vertexArray,
const Array< Vector2 > &  texCoordArray,
const Array< Vector3 > &  vertexNormalArray,
const Array< Face > &  faceArray,
Array< Vector3 > &  tangent,
Array< Vector3 > &  binormal 
)
static

Computes tangent and binormal vectors, which provide a (mostly) consistent parameterization over the surface for effects like bump mapping. In the resulting coordinate frame, T = x (varies with texture s coordinate), B = y (varies with negative texture t coordinate), and N = z for a right-handed coordinate frame. If a billboard is vertical on the screen in view of the camera, the tangent space matches the camera's coordinate frame.

The vertex, texCoord, tangent, and binormal arrays are parallel arrays.

The resulting tangent and binormal might not be exactly perpendicular to each other. They are guaranteed to be perpendicular to the normal.

[Max] McGuire

558  {
559 
560  debugAssertM(faceArray.size() != 0, "Unable to calculate valid tangent space without faces.");
561 
562  tangent.resize(vertexArray.size());
563  binormal.resize(vertexArray.size());
564 
565  // Zero the output arrays.
566  System::memset(tangent.getCArray(), 0, sizeof(Vector3) * tangent.size());
567  System::memset(binormal.getCArray(), 0, sizeof(Vector3) * binormal.size());
568 
569  // Iterate over faces, computing the tangent vectors for each
570  // vertex. Accumulate those into the tangent and binormal arrays
571  // and then orthonormalize at the end.
572 
573  for (int f = 0; f < faceArray.size(); ++f) {
574  const Face& face = faceArray[f];
575 
576  const int i0 = face.vertexIndex[0];
577  const int i1 = face.vertexIndex[1];
578  const int i2 = face.vertexIndex[2];
579 
580  const Vector3& v0 = vertexArray[i0];
581  const Vector3& v1 = vertexArray[i1];
582  const Vector3& v2 = vertexArray[i2];
583 
584  const Vector2& t0 = texCoordArray[i0];
585  const Vector2& t1 = texCoordArray[i1];
586  const Vector2& t2 = texCoordArray[i2];
587 
588  // See http://www.terathon.com/code/tangent.html for a derivation of the following code
589 
590  // vertex edges
591  Vector3 ve1 = v1 - v0;
592  Vector3 ve2 = v2 - v0;
593 
594  // texture edges
595  Vector2 te1 = t1 - t0;
596  Vector2 te2 = t2 - t0;
597 
598  Vector3 n(ve1.cross(ve2).direction());
599  Vector3 t, b;
600 
601  float r = te1.x * te2.y - te1.y * te2.x;
602  if (r == 0.0) {
603  // degenerate case
604  if (! n.isFinite() || n.isZero()) {
605  n = Vector3::unitY();
606  }
607  n.getTangents(t, b);
608  } else {
609  r = 1.0f / r;
610  t = (te2.y * ve1 - te1.y * ve2) * r;
611  b = (te2.x * ve1 - te1.x * ve2) * r;
612  }
613 
614  for (int v = 0; v < 3; ++v) {
615  int i = face.vertexIndex[v];
616  tangent[i] += t;
617  binormal[i] += b;
618  }
619  }
620 
621  // Normalize the basis vectors
622  for (int v = 0; v < vertexArray.size(); ++v) {
623  // Remove the component parallel to the normal
624  const Vector3& N = vertexNormalArray[v];
625  Vector3& T = tangent[v];
626  Vector3& B = binormal[v];
627 
628  debugAssertM(N.isUnit() || N.isZero(), "Input normals must have unit length");
629 
630  T -= T.dot(N) * N;
631  B -= B.dot(N) * N;
632 
633  // Normalize
634  T = T.directionOrZero();
635  B = B.directionOrZero();
636  }
637 }
static void memset(void *dst, uint8 value, size_t numBytes)
Definition: System.cpp:695
#define debugAssertM(exp, message)
Definition: debugAssert.h:161
static const Vector3 & unitY()
Definition: Vector3.cpp:122
float tangent(float x)
Definition: Spell.cpp:1502

+ Here is the call graph for this function:

void G3D::MeshAlg::computeWeld ( const Array< Vector3 > &  oldVertexPositions,
Array< Vector3 > &  newVertexPositions,
Array< int > &  toNew,
Array< int > &  toOld,
float  radius = fuzzyEpsilon32 
)
static

Welds nearby and colocated elements of the oldVertexArray together so that newVertexArray contains no vertices within radius of one another. Every vertex in newVertexPositions also appears in oldVertexPositions. This is useful for downsampling meshes and welding cracks created by artist errors or numerical imprecision.

The two integer arrays map indices back and forth between the arrays according to:

oldVertexArray[toOld[ni]] == newVertexArray[ni]
oldVertexArray[oi] == newVertexArray[toNew[ni]]

Note that newVertexPositions is never longer than oldVertexPositions and is shorter when vertices are welded.

Welding with a large radius will effectively compute a lower level of detail for the mesh.

The welding method runs in roughly linear time in the length of oldVertexArray– a uniform spatial grid is used to achieve nearly constant time vertex collapses for uniformly distributed vertices.

It is sometimes desirable to keep the original vertex ordering but identify the unique vertices. The following code computes array canonical s.t. canonical[v] = first occurance of a vertex near oldVertexPositions[v] in oldVertexPositions.

   Array<int> canonical(oldVertexPositions.size()), toNew, toOld;
   computeWeld(oldVertexPositions, Array<Vector3>(), toNew, toOld, radius);
   for (int v = 0; v < canonical.size(); ++v) {
       canonical[v] = toOld[toNew[v]];
   }

See also G3D::MeshAlg::weldAdjacency.

[The] method is that described as the 'Grouper' in Baum, Mann, Smith, and Winget, Making Radiosity Usable: Automatic Preprocessing and Meshing Techniques for the Generation of Accurate Radiosity Solutions, Computer Graphics vol 25, no 4, July 1991.

Deprecated:
Use weld.
208  {
209 
210  shared_ptr<_internal::Welder> welder = shared_ptr<_internal::Welder> (new _internal::Welder(oldVertexArray, newVertexArray, toNew, toOld, radius));
211  welder->weld();
212 }

+ Here is the caller graph for this function:

int G3D::MeshAlg::countBoundaryEdges ( const Array< Edge > &  edgeArray)
static

Counts the number of edges (in an edge array returned from MeshAlg::computeAdjacency) that have only one adjacent face.

401  {
402  int b = 0;
403 
404  for (int i = 0; i < edgeArray.size(); ++i) {
405  if ((edgeArray[i].faceIndex[0] == MeshAlg::Face::NONE) !=
406  (edgeArray[i].faceIndex[1] == MeshAlg::Face::NONE)) {
407  ++b;
408  }
409  }
410 
411  return b;
412 }
static const int NONE
Definition: MeshAlg.h:103

+ Here is the call graph for this function:

void G3D::MeshAlg::createIndexArray ( int  n,
Array< int > &  array,
int  start = 0,
int  run = 1,
int  skip = 0 
)
static

Generates an array of integers from start to start + n - 1 that have run numbers in series then omit the next skip before the next run. Useful for turning a triangle list into an indexed face set.

Example:

  createIndexArray(10, x);
  // x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  createIndexArray(5, x, 2);
  // x = [2, 3, 4, 5, 6, 7]
  createIndexArray(6, x, 0, 2, 1);
  // x = [0, 1, 3, 4, 6, 7]
316  {
317  debugAssert(skip >= 0);
318  debugAssert(run >= 0);
319 
320  array.resize(n);
321  if (skip == 0) {
322  for (int i = 0; i < n; ++i) {
323  array[i] = start + i;
324  }
325  } else {
326  int rcount = 0;
327  int j = start;
328  for (int i = 0; i < n; ++i) {
329  array[i] = j;
330 
331  ++j;
332  ++rcount;
333 
334  if (rcount == run) {
335  rcount = 0;
336  j += skip;
337  }
338  }
339  }
340 }
void resize(size_t n, bool shrinkIfNecessary=true)
Definition: Array.h:490
#define debugAssert(exp)
Definition: debugAssert.h:160

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

void G3D::MeshAlg::debugCheckConsistency ( const Array< Face > &  faceArray,
const Array< Edge > &  edgeArray,
const Array< Vertex > &  vertexArray 
)
static

In debug mode, asserts that the adjacency references between the face, edge, and vertex array are consistent.

696  {
697 
698 #ifdef _DEBUG
699  for (int v = 0; v < vertexArray.size(); ++v) {
700  const MeshAlg::Vertex& vertex = vertexArray[v];
701 
702  for (int i = 0; i < vertex.edgeIndex.size(); ++i) {
703  const int e = vertex.edgeIndex[i];
704  debugAssert(edgeArray[(e >= 0) ? e : ~e].containsVertex(v));
705  }
706 
707  for (int i = 0; i < vertex.faceIndex.size(); ++i) {
708  const int f = vertex.faceIndex[i];
709  debugAssert(faceArray[f].containsVertex(v));
710  }
711 
712  }
713 
714  for (int e = 0; e < edgeArray.size(); ++e) {
715  const MeshAlg::Edge& edge = edgeArray[e];
716 
717  for (int i = 0; i < 2; ++i) {
718  debugAssert((edge.faceIndex[i] == MeshAlg::Face::NONE) ||
719  faceArray[edge.faceIndex[i]].containsEdge(e));
720 
721  debugAssert(vertexArray[edge.vertexIndex[i]].inEdge(e));
722  }
723  }
724 
725  // Every face's edge must be on that face
726  for (int f = 0; f < faceArray.size(); ++f) {
727  const MeshAlg::Face& face = faceArray[f];
728  for (int i = 0; i < 3; ++i) {
729  int e = face.edgeIndex[i];
730  int ei = (e >= 0) ? e : ~e;
731  debugAssert(edgeArray[ei].inFace(f));
732 
733  // Make sure the edge is oriented appropriately
734  if (e >= 0) {
735  debugAssert(edgeArray[ei].faceIndex[0] == (int)f);
736  } else {
737  debugAssert(edgeArray[ei].faceIndex[1] == (int)f);
738  }
739 
740  debugAssert(vertexArray[face.vertexIndex[i]].inFace(f));
741  }
742  }
743 #else
744  (void)faceArray;
745  (void)edgeArray;
746  (void)vertexArray;
747 #endif // _DEBUG
748 }
static const int NONE
Definition: MeshAlg.h:103
#define debugAssert(exp)
Definition: debugAssert.h:160

+ Here is the call graph for this function:

static int G3D::MeshAlg::findEdgeIndex ( const Array< Vector3 > &  vertexArray,
Array< Edge > &  geometricEdgeArray,
int  i0,
int  i1,
int  f,
double  area 
)
staticprotected

Helper for computeAdjacency. If a directed edge with index e already exists from i0 to i1 then e is returned. If a directed edge with index e already exists from i1 to i0, ~e is returned (the complement) and edgeArray[e] is set to f. Otherwise, a new edge is created from i0 to i1 with first face index f and its index is returned.

Parameters
vertexArrayVertex positions to use when deciding colocation.
areaArea of face f. When multiple edges of the same direction are found between the same vertices (usually because of degenerate edges) the face with larger area is kept in the edge table.
void G3D::MeshAlg::generateGrid ( Array< Vector3 > &  vertex,
Array< Vector2 > &  texCoord,
Array< int > &  index,
int  wCells = 10,
int  hCells = 10,
const Vector2 textureScale = Vector2(1,1),
bool  spaceCentered = true,
bool  twoSided = true,
const CoordinateFrame xform = CoordinateFrame(),
const Image1::Ref elevation = Image1::Ref() 
)
static

Generates a unit square in the X-Z plane composed of a grid of wCells x hCells squares on the unit interval and then transforms it by xform.

Parameters
vertexOutput vertices
texCoordOutput texture coordinates
indexOutput triangle list indices
textureScaleLower-right texture coordinate
spaceCenteredIf true, the coordinates generated are centered at the origin before the transformation.
twoSidedIf true, matching top and bottom planes are generated.
elevationIf non-NULL, values from this image are used as elevations. Apply an xform to adjust the scale
38  {
39 
40  vertex.fastClear();
41  texCoord.fastClear();
42  index.fastClear();
43 
44  // Generate vertices
45  for (int z = 0; z <= hCells; ++z) {
46  for (int x = 0; x <= wCells; ++x) {
47  Vector3 v(x / (float)wCells, 0, z / (float)hCells);
48 
49  Vector2 t = v.xz() * textureScale;
50 
51  texCoord.append(t);
52 
53  if (height) {
54  v.y = height->nearest(v.x * (height->width() - 1), v.z * (height->height() - 1)).value;
55  }
56  if (spaceCentered) {
57  v -= Vector3(0.5f, 0, 0.5f);
58  }
59  v = xform.pointToWorldSpace(v);
60  vertex.append(v);
61  }
62  }
63 
64  // Generate indices
65  for (int z = 0; z < hCells; ++z) {
66  for (int x = 0; x < wCells; ++x) {
67  int A = x + z * (wCells + 1);
68  int B = A + 1;
69  int C = A + (wCells + 1);
70  int D = C + 1;
71 
72  // A B
73  // *-----*
74  // | \ |
75  // | \ |
76  // *-----*
77  // C D
78 
79  index.append(A, D, B);
80  index.append(A, C, D);
81  }
82  }
83 
84  if (twoSided) {
85  // The index array needs to have reversed winding for the bottom
86  // and offset by the original number of vertices
87  Array<int> ti = index;
88  ti.reverse();
89  for (int i = 0; i < ti.size(); ++i) {
90  ti[i] += vertex.size();
91  }
92  index.append(ti);
93 
94  // Duplicate the arrays
95  vertex.append(Array<Vector3>(vertex));
96  texCoord.append(Array<Vector2>(texCoord));
97  }
98 }
void fastClear()
Definition: Array.h:419
void reverse()
Definition: Array.h:1067
G3D::int16 z
Definition: Vector3int16.h:46
G3D::int16 x
Definition: Vector2int16.h:37
void append(const T &value)
Definition: Array.h:583

+ Here is the call graph for this function:

void G3D::MeshAlg::identifyBackfaces ( const Array< Vector3 > &  vertexArray,
const Array< Face > &  faceArray,
const Vector4 P,
Array< bool > &  backface 
)
static

Classifies each face as a backface or a front face relative to the observer point P (which is at infinity when P.w = 0). A face with normal exactly perpendicular to the observer vector may be classified as either a front or a back face arbitrarily.

250  {
251 
252  Vector3 P = HP.xyz();
253 
254  backface.resize(faceArray.size());
255 
256  if (fuzzyEq(HP.w, 0.0f)) {
257  // Infinite case
258  for (int f = faceArray.size() - 1; f >= 0; --f) {
259  const MeshAlg::Face& face = faceArray[f];
260 
261  const Vector3& v0 = vertexArray[face.vertexIndex[0]];
262  const Vector3& v1 = vertexArray[face.vertexIndex[1]];
263  const Vector3& v2 = vertexArray[face.vertexIndex[2]];
264 
265  const Vector3 N = (v1 - v0).cross(v2 - v0);
266 
267  backface[f] = N.dot(P) < 0;
268  }
269  } else {
270  // Finite case
271  for (int f = faceArray.size() - 1; f >= 0; --f) {
272  const MeshAlg::Face& face = faceArray[f];
273 
274  const Vector3& v0 = vertexArray[face.vertexIndex[0]];
275  const Vector3& v1 = vertexArray[face.vertexIndex[1]];
276  const Vector3& v2 = vertexArray[face.vertexIndex[2]];
277 
278  const Vector3 N = (v1 - v0).cross(v2 - v0);
279 
280  backface[f] = N.dot(P - v0) < 0;
281  }
282  }
283 }
float __fastcall dot(const Vector3 &rkVector) const
Definition: Vector3.h:771
Vector3 cross(const Vector3 &v1, const Vector3 &v2)
Definition: vectorMath.h:144
uint8 const P[]
Definition: AuthenticationPackets.cpp:225
bool fuzzyEq(double a, double b)
Definition: g3dmath.h:857

+ Here is the call graph for this function:

void G3D::MeshAlg::identifyBackfaces ( const Array< Vector3 > &  vertexArray,
const Array< Face > &  faceArray,
const Vector4 P,
Array< bool > &  backface,
const Array< Vector3 > &  faceNormals 
)
static

A faster version of identifyBackfaces for the case where face normals have already been computed

291  {
292 
293  Vector3 P = HP.xyz();
294 
295  backface.resize(faceArray.size());
296 
297  if (fuzzyEq(HP.w, 0.0f)) {
298  // Infinite case
299  for (int f = faceArray.size() - 1; f >= 0; --f) {
300  const Vector3& N = faceNormals[f];
301  backface[f] = N.dot(P) < 0;
302  }
303  } else {
304  // Finite case
305  for (int f = faceArray.size() - 1; f >= 0; --f) {
306  const MeshAlg::Face& face = faceArray[f];
307  const Vector3& v0 = vertexArray[face.vertexIndex[0]];
308  const Vector3& N = faceNormals[f];
309 
310  backface[f] = N.dot(P - v0) < 0;
311  }
312  }
313 }
uint8 const P[]
Definition: AuthenticationPackets.cpp:225
bool fuzzyEq(double a, double b)
Definition: g3dmath.h:857

+ Here is the call graph for this function:

template<class IndexType >
static void G3D::MeshAlg::toIndexedTriList ( const Array< IndexType > &  inIndices,
MeshAlg::Primitive  inType,
Array< IndexType > &  outIndices 
)
inlinestatic

Converts quadlist (QUADS), triangle fan (TRIANGLE_FAN), tristrip(TRIANGLE_STRIP), and quadstrip (QUAD_STRIP) indices into triangle list (TRIANGLES) indices and appends them to outIndices.

569  {
570 
571  debugAssert(
572  inType == PrimitiveType::TRIANGLE_STRIP ||
573  inType == PrimitiveType::TRIANGLE_FAN ||
574  inType == PrimitiveType::QUADS ||
575  inType == PrimitiveType::QUAD_STRIP);
576 
577  const int inSize = inIndices.size();
578 
579  switch(inType) {
581  {
582  debugAssert(inSize >= 3);
583 
584  int N = outIndices.size();
585  outIndices.resize(N + (inSize - 2) * 3);
586 
587  for (IndexType i = 1, outIndex = N; i <= (inSize - 2); ++i, outIndex += 3) {
588  outIndices[outIndex] = inIndices[0];
589  outIndices[outIndex + 1] = inIndices[i];
590  outIndices[outIndex + 2] = inIndices[i + 1];
591  }
592 
593  break;
594  }
595 
597  {
598  debugAssert(inSize >= 3);
599 
600  int N = outIndices.size();
601  outIndices.resize(N + (inSize - 2) * 3);
602 
603  bool atEven = false;
604  for (IndexType i = 0, outIndex = N; i < (inSize - 2); ++i, outIndex += 3) {
605  if (atEven) {
606  outIndices[outIndex] = inIndices[i + 1];
607  outIndices[outIndex + 1] = inIndices[i];
608  outIndices[outIndex + 2] = inIndices[i + 2];
609  atEven = false;
610  } else {
611  outIndices[outIndex] = inIndices[i];
612  outIndices[outIndex + 1] = inIndices[i + 1];
613  outIndices[outIndex + 2] = inIndices[i + 2];
614  atEven = true;
615  }
616  }
617 
618  break;
619  }
620 
622  {
623  debugAssert(inIndices.size() >= 4);
624 
625  int N = outIndices.size();
626  outIndices.resize(N + (inSize / 4) * 3);
627 
628  for (IndexType i = 0, outIndex = N; i <= (inSize - 4); i += 4, outIndex += 6) {
629  outIndices[outIndex] = inIndices[i];
630  outIndices[outIndex + 1] = inIndices[i + 1];
631  outIndices[outIndex + 2] = inIndices[i + 3];
632  outIndices[outIndex + 3] = inIndices[i + 1];
633  outIndices[outIndex + 4] = inIndices[i + 2];
634  outIndices[outIndex + 5] = inIndices[i + 3];
635  }
636 
637  break;
638  }
639 
641  {
642  debugAssert(inIndices.size() >= 4);
643 
644  int N = outIndices.size();
645  outIndices.resize(N + (inSize - 2) * 3);
646 
647  for (IndexType i = 0, outIndex = N; i <= (inSize - 2); i += 2, outIndex += 6) {
648  outIndices[outIndex] = inIndices[i];
649  outIndices[outIndex + 1] = inIndices[i + 1];
650  outIndices[outIndex + 2] = inIndices[i + 2];
651  outIndices[outIndex + 3] = inIndices[i + 2];
652  outIndices[outIndex + 4] = inIndices[i + 1];
653  outIndices[outIndex + 5] = inIndices[i + 3];
654  }
655  break;
656  }
657  default:
658  alwaysAssertM(false, "Illegal argument");
659  }
660  }
Definition: constants.h:28
Definition: constants.h:26
Definition: constants.h:29
#define debugAssert(exp)
Definition: debugAssert.h:160
Definition: constants.h:27
#define alwaysAssertM(exp, message)
Definition: debugAssert.h:165

+ Here is the call graph for this function:

void G3D::MeshAlg::weldAdjacency ( const Array< Vector3 > &  originalGeometry,
Array< Face > &  faceArray,
Array< Edge > &  edgeArray,
Array< Vertex > &  vertexArray,
float  radius = fuzzyEpsilon32 
)
static

Modifies the face, edge, and vertex arrays in place so that colocated (within radius) vertices are treated as identical. Note that the vertexArray and corresponding geometry will contain elements that are no longer used. In the vertexArray, these elements are initialized to MeshAlg::Vertex() but not removed (because removal would change the indexing).

This is a good preprocessing step for algorithms that are only concerned with the shape of a mesh (e.g. cartoon rendering, fur, shadows) and not the indexing of the vertices.

Use this method when you have already computed adjacency information and want to collapse colocated vertices within that data without disturbing the actual mesh vertices or indexing scheme.

If you have not computed adjacency already, use MeshAlg::computeWeld instead and compute adjacency information after welding.

Deprecated:
Use weld.
Parameters
faceArrayMutated in place. Size is maintained (degenerate faces are not removed).
edgeArrayMutated in place. May shrink if boundary edges are welded together.
vertexArrayMutated in place. Size is maintained (duplicate vertices contain no adjacency info).
628  {
629 
630  // Num vertices
631  const int n = originalGeometry.size();
632 
633  // canonical[v] = first occurance of any vertex near oldVertexArray[v]
634  Array<int> canonical;
635  canonical.resize(n);
636 
637  Array<int> toNew, toOld;
638  // Throw away the new vertex array
639  Array<Vector3> dummy;
640  computeWeld(originalGeometry, dummy, toNew, toOld, radius);
641 
642  for (int v = 0; v < canonical.size(); ++v) {
643  // Round-trip through the toNew/toOld process. This will give
644  // us the original vertex.
645  canonical[v] = toOld[toNew[v]];
646  }
647 
648  // Destroy vertexArray (we reconstruct it below)
649  vertexArray.clear();
650  vertexArray.resize(n);
651 
652  bool hasBoundaryEdges = false;
653 
654  // Fix edge vertex indices
655  for (int e = 0; e < edgeArray.size(); ++e) {
656  Edge& edge = edgeArray[e];
657 
658  const int v0 = canonical[edge.vertexIndex[0]];
659  const int v1 = canonical[edge.vertexIndex[1]];
660 
661  edge.vertexIndex[0] = v0;
662  edge.vertexIndex[1] = v1;
663 
664  vertexArray[v0].edgeIndex.append(e);
665  vertexArray[v1].edgeIndex.append(~e);
666 
667  hasBoundaryEdges = hasBoundaryEdges || edge.boundary();
668  }
669 
670  // Fix face vertex indices
671  for (int f = 0; f < faceArray.size(); ++f) {
672  Face& face = faceArray[f];
673  for (int i = 0; i < 3; ++i) {
674  const int v = canonical[face.vertexIndex[i]];
675 
676  face.vertexIndex[i] = v;
677 
678  // Add the back pointer
679  vertexArray[v].faceIndex.append(f);
680  }
681  }
682 
683  if (hasBoundaryEdges) {
684  // As a result of the welding, some of the boundary edges at
685  // the end of the array may now have mates and no longer be
686  // boundaries. Try to pair these up.
687 
688  weldBoundaryEdges(faceArray, edgeArray, vertexArray);
689  }
690 }
static void computeWeld(const Array< Vector3 > &oldVertexPositions, Array< Vector3 > &newVertexPositions, Array< int > &toNew, Array< int > &toOld, float radius=fuzzyEpsilon32)
Definition: MeshAlgWeld.cpp:203
static void weldBoundaryEdges(Array< Face > &faceArray, Array< Edge > &edgeArray, Array< Vertex > &vertexArray)
Definition: MeshAlgAdjacency.cpp:430

+ Here is the call graph for this function:

void G3D::MeshAlg::weldBoundaryEdges ( Array< Face > &  faceArray,
Array< Edge > &  edgeArray,
Array< Vertex > &  vertexArray 
)
staticprivate

Helper for weldAdjacency

433  {
434 
435  // Copy over the original edge array
436  Array<Edge> oldEdgeArray = edgeArray;
437 
438  // newEdgeIndex[e] is the new index of the old edge with index e
439  // Note that newEdgeIndex[e] might be negative, indicating that
440  // the edge switched direction between the arrays.
441  Array<int> newEdgeIndex;
442  newEdgeIndex.resize(edgeArray.size());
443  edgeArray.resize(0);
444 
445  // boundaryEdgeIndices[v_low] is an array of the indices of
446  // all boundary edges whose lower vertex is v_low.
447  Table<int, Array<int> > boundaryEdgeIndices;
448 
449  // Copy over non-boundary edges to the new array
450  for (int e = 0; e < oldEdgeArray.size(); ++e) {
451  if (oldEdgeArray[e].boundary()) {
452 
453  // Add to the boundary table
454  const int v_low = iMin(oldEdgeArray[e].vertexIndex[0], oldEdgeArray[e].vertexIndex[1]);
455  if (! boundaryEdgeIndices.containsKey(v_low)) {
456  boundaryEdgeIndices.set(v_low, Array<int>());
457  }
458  boundaryEdgeIndices[v_low].append(e);
459 
460  // We'll fill out newEdgeIndex[e] later, when we find pairs
461 
462  } else {
463 
464  // Copy the edge to the new array
465  newEdgeIndex[e] = edgeArray.size();
466  edgeArray.append(oldEdgeArray[e]);
467 
468  }
469  }
470 
471 
472  // Remove all edges from the table that have pairs.
473  Table<int, Array<int> >::Iterator cur = boundaryEdgeIndices.begin();
474  Table<int, Array<int> >::Iterator end = boundaryEdgeIndices.end();
475  while (cur != end) {
476  Array<int>& boundaryEdge = cur->value;
477 
478  for (int i = 0; i < boundaryEdge.size(); ++i) {
479  int ei = boundaryEdge[i];
480  const Edge& edgei = oldEdgeArray[ei];
481 
482  for (int j = i + 1; j < boundaryEdge.size(); ++j) {
483  int ej = boundaryEdge[j];
484  const Edge& edgej = oldEdgeArray[ej];
485 
486  // See if edge ei is the reverse (match) of edge ej.
487 
488  // True if the edges match
489  bool match = false;
490 
491  // True if edgej's vertex indices are reversed from
492  // edgei's (usually true).
493  bool reversej = false;
494 
495  int u = edgei.vertexIndex[0];
496  int v = edgei.vertexIndex[1];
497 
498  if (edgei.faceIndex[0] != Face::NONE) {
499  // verts|faces
500  // edgei = [u v A /]
501 
502  if (edgej.faceIndex[0] != Face::NONE) {
503  if ((edgej.vertexIndex[0] == v) && (edgej.vertexIndex[1] == u)) {
504  // This is the most common of the four cases
505 
506  // edgej = [v u B /]
507  match = true;
508  reversej = true;
509  }
510  } else {
511  if ((edgej.vertexIndex[0] == u) && (edgej.vertexIndex[1] == v)) {
512  // edgej = [u v / B]
513  match = true;
514  }
515  }
516  } else {
517  // edgei = [u v / A]
518  if (edgej.faceIndex[0] != Face::NONE) {
519  if ((edgej.vertexIndex[0] == u) && (edgej.vertexIndex[1] == v)) {
520  // edgej = [u v B /]
521  match = true;
522  }
523  } else {
524  if ((edgej.vertexIndex[0] == v) && (edgej.vertexIndex[1] == u)) {
525  // edgej = [v u / B]
526  match = true;
527  reversej = true;
528  }
529  }
530  }
531 
532  if (match) {
533  // ei and ej can be paired as a single edge
534  int e = edgeArray.size();
535  Edge& edge = edgeArray.next();
536 
537  // Follow the direction of edgei.
538  edge = edgei;
539  newEdgeIndex[ei] = e;
540 
541  // Insert the face index for edgej.
542  int fj = edgej.faceIndex[0];
543  if (fj == Face::NONE) {
544  fj = edgej.faceIndex[1];
545  }
546 
547  if (edge.faceIndex[0] == Face::NONE) {
548  edge.faceIndex[0] = fj;
549  } else {
550  edge.faceIndex[1] = fj;
551  }
552 
553  if (reversej) {
554  // The new edge is backwards of the old edge for ej
555  newEdgeIndex[ej] = ~e;
556  } else {
557  newEdgeIndex[ej] = e;
558  }
559 
560  // Remove both ei and ej from being candidates for future pairing.
561  // Remove ej first since it comes later in the list (removing
562  // ei would decrease the index of ej to j - 1).
563  boundaryEdge.fastRemove(j);
564  boundaryEdge.fastRemove(i);
565 
566  // Re-process element i, which is now a new edge index
567  --i;
568 
569  // Jump out of the j for-loop
570  break;
571  }
572  }
573  }
574  ++cur;
575  }
576 
577  // Anything remaining in the table is a real boundary edge; just copy it to
578  // the end of the array.
579  cur = boundaryEdgeIndices.begin();
580  end = boundaryEdgeIndices.end();
581  while (cur != end) {
582  Array<int>& boundaryEdge = cur->value;
583 
584  for (int b = 0; b < boundaryEdge.size(); ++b) {
585  const int e = boundaryEdge[b];
586 
587  newEdgeIndex[e] = edgeArray.size();
588  edgeArray.append(oldEdgeArray[e]);
589  }
590 
591  ++cur;
592  }
593 
594  // Finally, fix up edge indices in the face and vertex arrays
595  for (int f = 0; f < faceArray.size(); ++f) {
596  Face& face = faceArray[f];
597  for (int i = 0; i < 3; ++i) {
598  int e = face.edgeIndex[i];
599 
600  if (e < 0) {
601  face.edgeIndex[i] = ~newEdgeIndex[~e];
602  } else {
603  face.edgeIndex[i] = newEdgeIndex[e];
604  }
605  }
606  }
607 
608  for (int v = 0; v < vertexArray.size(); ++v) {
609  Vertex& vertex = vertexArray[v];
610  for (int i = 0; i < vertex.edgeIndex.size(); ++i) {
611  int e = vertex.edgeIndex[i];
612 
613  if (e < 0) {
614  vertex.edgeIndex[i] = ~newEdgeIndex[~e];
615  } else {
616  vertex.edgeIndex[i] = newEdgeIndex[e];
617  }
618  }
619  }
620 }
static const int NONE
Definition: MeshAlg.h:103
int iMin(int x, int y)
Definition: g3dmath.h:753

+ Here is the call graph for this function:

+ Here is the caller graph for this function:


The documentation for this class was generated from the following files: