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

Public Member Functions

void process (Array< Vector3 > &vertexArray, Array< Vector2 > &texCoordArray, Array< Vector3 > &normalArray, Array< Array< int > * > &indexArrayArray, float normAngle, float texRadius, float normRadius)
 
 WeldHelper (float vertRadius)
 

Private Member Functions

int getIndex (const Vector3 &v, const Vector3 &n, const Vector2 &t)
 
void updateTriLists (Array< Array< int > * > &indexArrayArray, const Array< Vector3 > &vertexArray, const Array< Vector3 > &normalArray, const Array< Vector2 > &texCoordArray)
 
void unroll (const Array< Array< int > * > &indexArrayArray, const Array< Vector3 > &vertexArray, const Array< Vector2 > &texCoordArray, Array< Vector3 > &unrolledVertexArray, Array< Vector2 > &unrolledTexCoordArray)
 
void computeFaceNormals (const Array< Vector3 > &vertexArray, Array< Vector3 > &faceNormalArray)
 
void smoothNormals (const Array< Point3 > &vertexArray, const Array< Vector3 > &normalArray, Array< Vector3 > &smoothNormalArray)
 

Private Attributes

PointHashGrid< VNTiweldGrid
 
Array< Vector3 > * outputVertexArray
 
Array< Vector3 > * outputNormalArray
 
Array< Vector2 > * outputTexCoordArray
 
float vertexWeldRadius
 
float normalWeldRadius2
 
float texCoordWeldRadius2
 
float normalSmoothingAngle
 

Constructor & Destructor Documentation

G3D::_internal::WeldHelper::WeldHelper ( float  vertRadius)
inline
459  :
460  weldGrid(max(vertRadius, 0.1f), AreaMemoryManager::create()),
461  vertexWeldRadius(vertRadius) {
462  }
static MemoryManager::Ref create()
Definition: MemoryManager.cpp:35
PointHashGrid< VNTi > weldGrid
Definition: Welder.cpp:83
T max(const T &x, const T &y)
Definition: g3dmath.h:320
float vertexWeldRadius
Definition: Welder.cpp:89

Member Function Documentation

void G3D::_internal::WeldHelper::computeFaceNormals ( const Array< Vector3 > &  vertexArray,
Array< Vector3 > &  faceNormalArray 
)
inlineprivate

For every three vertices, compute the face normal and store it three times. Sliver triangles have a zero surface normal, which we will later take to match any surface normal.

224  {
225 # ifdef VERBOSE
226  debugPrintf("WeldHelper::computeFaceNormals\n");
227 # endif
228 
229  debugAssertM(vertexArray.size() % 3 == 0, "Input is not a triangle soup");
230  debugAssertM(faceNormalArray.size() == 0, "Output must start empty.");
231 
232  for (int v = 0; v < vertexArray.size(); v += 3) {
233  const Vector3& e0 = vertexArray[v + 1] - vertexArray[v];
234  const Vector3& e1 = vertexArray[v + 2] - vertexArray[v];
235 
236  // Note that the length may be zero in the case of sliver polygons, e.g.,
237  // those correcting a T-junction. Scale up by 256 to avoid underflow when
238  // multiplying very small edges
239  const Vector3& n = (e0.cross(e1 * 256.0f)).directionOrZero();
240 
241  // Append the normal once per vertex.
242  faceNormalArray.append(n, n, n);
243  }
244  }
std::string __cdecl debugPrintf(const char *fmt...) G3D_CHECK_PRINTF_ARGS
Definition: debugAssert.cpp:355
#define debugAssertM(exp, message)
Definition: debugAssert.h:161

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

int G3D::_internal::WeldHelper::getIndex ( const Vector3 v,
const Vector3 n,
const Vector2 t 
)
inlineprivate

Returns the index of the vertex in outputVertexArray/outputNormalArray/outputTexCoordArray that is within the global tolerances of v,n,t. If there is no such vertex, adds it to the arrays and returns that index.

Called from updateTriLists().

104  {
105  PointHashGrid<VNTi>::SphereIterator it =
106  weldGrid.begin(Sphere(v, vertexWeldRadius));
107 
108  if (n.isZero()) {
109  // Don't bother trying to match the surface normal, since this vertex has no surface normal.
110  while (it.isValid()) {
111  if ((t - it->texCoord).squaredLength() <= texCoordWeldRadius2) {
112  // This is the vertex
113  return it->index;
114  }
115  ++it;
116  }
117  } else {
118  while (it.isValid()) {
119  if (((n - it->normal).squaredLength() <= normalWeldRadius2) &&
120  ((t - it->texCoord).squaredLength() <= texCoordWeldRadius2)) {
121  // This is the vertex
122  return it->index;
123  }
124  ++it;
125  }
126  }
127 
128  // Note that a sliver triangle processed before its neighbors may reach here
129  // with a zero length normal.
130 
131  // The vertex does not exist. Create it.
132  const int i = outputVertexArray->size();
133  outputVertexArray->append(v);
134  outputNormalArray->append(n);
135  outputTexCoordArray->append(t);
136 
137  // Store in the grid so that it will be remembered.
138  weldGrid.insert(VNTi(v, n, t, i));
139 
140  return i;
141  }
PointHashGrid< VNTi > weldGrid
Definition: Welder.cpp:83
Array< Vector3 > * outputNormalArray
Definition: Welder.cpp:86
Array< Vector2 > * outputTexCoordArray
Definition: Welder.cpp:87
float vertexWeldRadius
Definition: Welder.cpp:89
float normalWeldRadius2
Definition: Welder.cpp:91
float texCoordWeldRadius2
Definition: Welder.cpp:92
Array< Vector3 > * outputVertexArray
Definition: Welder.cpp:85

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

void G3D::_internal::WeldHelper::process ( Array< Vector3 > &  vertexArray,
Array< Vector2 > &  texCoordArray,
Array< Vector3 > &  normalArray,
Array< Array< int > * > &  indexArrayArray,
float  normAngle,
float  texRadius,
float  normRadius 
)
inline

Algorithm:

  1. Unroll the indexed triangle list into a triangle list, where there are duplicated vertices.
  2. Compute face normals for all triangles, and expand those into the triangle vertices.
  3. At each vertex, average all normals that are within normalSmoothingAngle.
  4. Generate output indexArrayArray. While doing so, merge all vertices where the distance between position, texCoord, and normal is within the thresholds.
396  {
397 # ifdef VERBOSE
398  debugPrintf("WeldHelper::process\n");
399 # endif
400 
401  normalSmoothingAngle = normAngle;
402  normalWeldRadius2 = square(normRadius);
403  texCoordWeldRadius2 = square(texRadius);
404 
405  const bool hasTexCoords = (texCoordArray.size() > 0);
406 
407  if (hasTexCoords) {
408  debugAssertM(vertexArray.size() == texCoordArray.size(),
409  "Input arrays are not parallel.");
410  }
411 
412  // Create an area memory manager for fast deallocation
413  Array<Vector3> unrolledVertexArray;
414  Array<Vector3> unrolledFaceNormalArray;
415  Array<Vector3> unrolledSmoothNormalArray;
416  Array<Vector2> unrolledTexCoordArray;
417 
418  unrolledVertexArray.reserve(vertexArray.size());
419  unrolledFaceNormalArray.reserve(vertexArray.size());
420  unrolledSmoothNormalArray.reserve(vertexArray.size());
421  unrolledTexCoordArray.reserve(vertexArray.size());
422 
423  if (! hasTexCoords) {
424  // Generate all zero texture coordinates
425  texCoordArray.resize(vertexArray.size());
426  }
427 
428  // Generate a flat (unrolled) triangle list with texture coordinates.
429  unroll(indexArrayArray, vertexArray, texCoordArray,
430  unrolledVertexArray, unrolledTexCoordArray);
431 
432  // Put the output back into the input slots.
433  outputVertexArray = &vertexArray;
434  outputNormalArray = &normalArray;
435  outputTexCoordArray = &texCoordArray;
436  outputVertexArray->fastClear();
437  outputNormalArray->fastClear();
438  outputTexCoordArray->fastClear();
439 
440  // For every three vertices, generate their face normal and store it at
441  // each vertex. The output array has the same length as the input.
442  computeFaceNormals(unrolledVertexArray, unrolledFaceNormalArray);
443 
444  // Compute smooth normals at vertices.
445  if (unrolledFaceNormalArray.size() > 0) {
446  smoothNormals(unrolledVertexArray, unrolledFaceNormalArray, unrolledSmoothNormalArray);
447  unrolledFaceNormalArray.clear();
448  }
449 
450  // Regenerate the triangle lists
451  updateTriLists(indexArrayArray, unrolledVertexArray, unrolledSmoothNormalArray, unrolledTexCoordArray);
452 
453  if (! hasTexCoords) {
454  // Throw away the generated texCoords
455  texCoordArray.resize(0);
456  }
457  }
std::string __cdecl debugPrintf(const char *fmt...) G3D_CHECK_PRINTF_ARGS
Definition: debugAssert.cpp:355
void computeFaceNormals(const Array< Vector3 > &vertexArray, Array< Vector3 > &faceNormalArray)
Definition: Welder.cpp:223
void unroll(const Array< Array< int > * > &indexArrayArray, const Array< Vector3 > &vertexArray, const Array< Vector2 > &texCoordArray, Array< Vector3 > &unrolledVertexArray, Array< Vector2 > &unrolledTexCoordArray)
Definition: Welder.cpp:196
#define debugAssertM(exp, message)
Definition: debugAssert.h:161
Array< Vector3 > * outputNormalArray
Definition: Welder.cpp:86
float normalSmoothingAngle
Definition: Welder.cpp:94
void updateTriLists(Array< Array< int > * > &indexArrayArray, const Array< Vector3 > &vertexArray, const Array< Vector3 > &normalArray, const Array< Vector2 > &texCoordArray)
Definition: Welder.cpp:151
Array< Vector2 > * outputTexCoordArray
Definition: Welder.cpp:87
double square(double fValue)
Definition: g3dmath.h:698
float normalWeldRadius2
Definition: Welder.cpp:91
void smoothNormals(const Array< Point3 > &vertexArray, const Array< Vector3 > &normalArray, Array< Vector3 > &smoothNormalArray)
Definition: Welder.cpp:251
float texCoordWeldRadius2
Definition: Welder.cpp:92
Array< Vector3 > * outputVertexArray
Definition: Welder.cpp:85

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

void G3D::_internal::WeldHelper::smoothNormals ( const Array< Point3 > &  vertexArray,
const Array< Vector3 > &  normalArray,
Array< Vector3 > &  smoothNormalArray 
)
inlineprivate

Computes smoothNormalArray, whose elements are those of normalArray averaged with neighbors within the angular cutoff.

253  {
254  if (normalSmoothingAngle <= 0) {
255  smoothNormalArray = normalArray;
256  return;
257  }
258 
259 # ifdef VERBOSE
260  debugPrintf("WeldHelper::smoothNormals\n");
261 # endif
262 
263  // Create an area memory manager for fast deallocation
264  MemoryManager::Ref mm = AreaMemoryManager::create(iRound(sizeof(VN) * normalArray.size() * 1.5));
265 
266  const float cosThresholdAngle = (float)cos(normalSmoothingAngle);
267 
268  debugAssert(vertexArray.size() == normalArray.size());
269  smoothNormalArray.resize(normalArray.size());
270 
271  if (vertexWeldRadius == 0) {
272  // Look for vertices with the exactly identical normal only
273 # ifdef VERBOSE
274  debugPrintf("Taking fast path\n");
275 # endif
276 
277  // Maximum expected faces that meet at a vertex
278  static const int k = 8;
279 
280  // Maps vertices to the indices of normals at that vertex
281  Table<Point3, SmallArray<Vector3, k> > normalTable;
282  for (int v = 0; v < vertexArray.size(); ++v) {
283  bool ignore = false;
284  SmallArray<Vector3, k>& list = normalTable.getCreate(vertexArray[v], ignore);
285  list.append(normalArray[v]);
286  }
287 
288  for (int v = 0; v < vertexArray.size(); ++v) {
289  Vector3 sum;
290 
291  const Vector3& original = normalArray[v];
292 
293  const SmallArray<Vector3, k>& list = normalTable[vertexArray[v]];
294 
295  for (int i = 0; i < list.size(); ++i) {
296  const Vector3& N = list[i];
297  const float cosAngle = N.dot(original);
298 
299  if (cosAngle > cosThresholdAngle) {
300  // This normal is close enough to consider. Avoid underflow by scaling up
301  sum += (N * 256.0f);
302  }
303  }
304 
305  const Vector3& average = sum.directionOrZero();
306 
307  const bool indeterminate = average.isZero();
308  // Never "smooth" a normal so far that it points backwards
309  const bool backFacing = original.dot(average) < 0;
310 
311  if (indeterminate || backFacing) {
312  // Revert to the face normal
313  smoothNormalArray[v] = original;
314  } else {
315  // Average available normals
316  smoothNormalArray[v] = average;
317  }
318  }
319 
320  } else {
321  // Non-zero vertex normal welding
322 # ifdef VERBOSE
323  debugPrintf("Taking slower weld path because vertexWeldRadius = %f\n",
325 # endif
326 
327  // Compute a hash grid so that we can find neighbors quickly.
328  alwaysAssertM(vertexWeldRadius > 0, "Cannot smooth with zero vertex weld radius");
329  PointHashGrid<VN> grid(vertexWeldRadius, mm);
330  for (int v = 0; v < normalArray.size(); ++v) {
331  grid.insert(VN(vertexArray[v], normalArray[v]));
332  }
333 
334  // OPT: this step could be done on multiple threads
335  for (int v = 0; v < normalArray.size(); ++v) {
336  // Compute the sum of all nearby normals within the cutoff angle.
337  // Search within the vertexWeldRadius, since those are the vertices
338  // that will collapse to the same point.
339  PointHashGrid<VN>::SphereIterator it =
340  grid.begin(Sphere(vertexArray[v], vertexWeldRadius));
341 
342  Vector3 sum;
343 
344  const Vector3& original = normalArray[v];
345  while (it.isValid()) {
346  const Vector3& N = it->normal;
347  const float cosAngle = N.dot(original);
348 
349  if (cosAngle > cosThresholdAngle) {
350  // This normal is close enough to consider. Avoid underflow by scaling up
351  sum += (N * 256.0f);
352  }
353  ++it;
354  }
355 
356  const Vector3& average = sum.directionOrZero();
357 
358  const bool indeterminate = average.isZero();
359  // Never "smooth" a normal so far that it points backwards
360  const bool backFacing = original.dot(average) < 0;
361 
362  if (indeterminate || backFacing) {
363  // Revert to the face normal
364  smoothNormalArray[v] = original;
365  } else {
366  // Average available normals
367  smoothNormalArray[v] = average;
368  }
369  }
370  }
371  }
std::string __cdecl debugPrintf(const char *fmt...) G3D_CHECK_PRINTF_ARGS
Definition: debugAssert.cpp:355
static MemoryManager::Ref create()
Definition: MemoryManager.cpp:35
int iRound(double fValue)
Definition: g3dmath.h:226
shared_ptr< class MemoryManager > Ref
Definition: MemoryManager.h:31
float normalSmoothingAngle
Definition: Welder.cpp:94
#define debugAssert(exp)
Definition: debugAssert.h:160
float vertexWeldRadius
Definition: Welder.cpp:89
#define alwaysAssertM(exp, message)
Definition: debugAssert.h:165

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

void G3D::_internal::WeldHelper::unroll ( const Array< Array< int > * > &  indexArrayArray,
const Array< Vector3 > &  vertexArray,
const Array< Vector2 > &  texCoordArray,
Array< Vector3 > &  unrolledVertexArray,
Array< Vector2 > &  unrolledTexCoordArray 
)
inlineprivate

Expands the indexed triangle lists into a triangle list.

Called from process()

200  {
201 
202 # ifdef VERBOSE
203  debugPrintf("WeldHelper::unroll\n");
204 # endif
205 
206  int numTriLists = indexArrayArray.size();
207  for (int t = 0; t < numTriLists; ++t) {
208  if (indexArrayArray[t] != NULL) {
209  const Array<int>& triList = *(indexArrayArray[t]);
210  for (int v = 0; v < triList.size(); ++v) {
211  int i = triList[v];
212  unrolledVertexArray.append(vertexArray[i]);
213  unrolledTexCoordArray.append(texCoordArray[i]);
214  }
215  }
216  }
217  }
std::string __cdecl debugPrintf(const char *fmt...) G3D_CHECK_PRINTF_ARGS
Definition: debugAssert.cpp:355
arena_t NULL
Definition: jemalloc_internal.h:624

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

void G3D::_internal::WeldHelper::updateTriLists ( Array< Array< int > * > &  indexArrayArray,
const Array< Vector3 > &  vertexArray,
const Array< Vector3 > &  normalArray,
const Array< Vector2 > &  texCoordArray 
)
inlineprivate

Updates each indexArray to refer to vertices in the outputVertexArray.

Called from process()

154  {
155 
156 # ifdef VERBOSE
157  debugPrintf("WeldHelper::updateTriLists\n");
158 # endif
159 
160  // Compute a hash grid so that we can find neighbors quickly.
161  // It begins empty and is extended as the tri lists are iterated
162  // through.
163  weldGrid.clear();
164 
165  // Process all triLists
166  int numTriLists = indexArrayArray.size();
167  int u = 0;
168  for (int t = 0; t < numTriLists; ++t) {
169  if (indexArrayArray[t] != NULL) {
170  Array<int>& triList = *(indexArrayArray[t]);
171 
172  // For all vertices in this list
173  for (int v = 0; v < triList.size(); ++v) {
174  // This vertex mapped to u in the flatVertexArray
175  triList[v] = getIndex(vertexArray[u], normalArray[u], texCoordArray[u]);
176 
177  /*
178  # ifdef G3D_DEBUG
179  {
180  int i = triList[v];
181  Vector3 N = normalArray[i];
182  debugAssertM(N.length() > 0.9f, "Produced non-unit normal");
183  }
184  # endif
185  */
186  ++u;
187  }
188  }
189  }
190  }
std::string __cdecl debugPrintf(const char *fmt...) G3D_CHECK_PRINTF_ARGS
Definition: debugAssert.cpp:355
PointHashGrid< VNTi > weldGrid
Definition: Welder.cpp:83
arena_t NULL
Definition: jemalloc_internal.h:624
int getIndex(const Vector3 &v, const Vector3 &n, const Vector2 &t)
Definition: Welder.cpp:104

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

Member Data Documentation

float G3D::_internal::WeldHelper::normalSmoothingAngle
private
float G3D::_internal::WeldHelper::normalWeldRadius2
private

Squared radius allowed for welding similar normals.

Array<Vector3>* G3D::_internal::WeldHelper::outputNormalArray
private
Array<Vector2>* G3D::_internal::WeldHelper::outputTexCoordArray
private
Array<Vector3>* G3D::_internal::WeldHelper::outputVertexArray
private
float G3D::_internal::WeldHelper::texCoordWeldRadius2
private
float G3D::_internal::WeldHelper::vertexWeldRadius
private
PointHashGrid<VNTi> G3D::_internal::WeldHelper::weldGrid
private

Used by getIndex and updateTriLists. Deallocating this is slow.


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