Developer Documentation
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
TangentSpace.cc
1 /*===========================================================================*\
2 * *
3 * OpenFlipper *
4 * Copyright (C) 2001-2014 by Computer Graphics Group, RWTH Aachen *
5 * www.openflipper.org *
6 * *
7 *--------------------------------------------------------------------------- *
8 * This file is part of OpenFlipper. *
9 * *
10 * OpenFlipper is free software: you can redistribute it and/or modify *
11 * it under the terms of the GNU Lesser General Public License as *
12 * published by the Free Software Foundation, either version 3 of *
13 * the License, or (at your option) any later version with the *
14 * following exceptions: *
15 * *
16 * If other files instantiate templates or use macros *
17 * or inline functions from this file, or you compile this file and *
18 * link it with other files to produce an executable, this file does *
19 * not by itself cause the resulting executable to be covered by the *
20 * GNU Lesser General Public License. This exception does not however *
21 * invalidate any other reasons why the executable file might be *
22 * covered by the GNU Lesser General Public License. *
23 * *
24 * OpenFlipper is distributed in the hope that it will be useful, *
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
27 * GNU Lesser General Public License for more details. *
28 * *
29 * You should have received a copy of the GNU LesserGeneral Public *
30 * License along with OpenFlipper. If not, *
31 * see <http://www.gnu.org/licenses/>. *
32 * *
33 \*===========================================================================*/
34 
35 /*===========================================================================*\
36 * *
37 * $Revision$ *
38 * $LastChangedBy$ *
39 * $Date$ *
40 * *
41 \*===========================================================================*/
42 
43 
45 
46 #include "TangentSpace.hh"
47 
49 
50 
51 #if QT_VERSION >= 0x050000
52  #include <QtWidgets>
53 #else
54  #include <QtGui>
55 #endif
56 #include <QVBoxLayout>
57 #include <QHash>
58 
59 
60 #include <map>
61 
62 #ifdef USE_OPENMP
63 #include <omp.h>
64 #endif
65 
66 
67 #ifdef ENABLE_EIGEN3
68 #include <Eigen/SVD>
69 #endif // ENABLE_EIGEN3
70 
71 #include <limits>
72 
73 
74 const float cmp_eps = std::numeric_limits<float>::epsilon();
75 
76 
77 /*
78 features:
79 - tangents are stored as float4, property name chosen by user (default: "inTangent" based on vertex shader input naming)
80 
81 - per vertex tangents computed as weighted average of TBN matrices of adjacent triangles
82 
83 - high quality per halfedge tangents are computed via smoothing groups
84  - smoothing group = group of incoming halfedges with the same normal, texcoord and parity
85  - weighted average only within the same smoothing group
86  - texture seams are preserved
87 
88 - 4th component stores parity of tangent space matrix
89  reconstruction in shader:
90  bitangent = cross(normal, tangent) * parity
91 
92 shader: transform from tangent space to object space:
93  vec3 t,b,n = tangent space matrix computed by this plugin
94  vec3 normalTS = sample normal map
95  vec3 normalOS = normalize( t * normalTS.x + b * normalTS.y + n * normalTS.z );
96 
97 
98 note: propvis plugin does not work with float4 props
99 
100 possible improvements
101 - compute custom smoothing groups via angle threshold (normals and uv angle, might require welding threshold)
102 - PolyMesh support
103 
104 reference: http://www.crytek.com/download/Triangle_mesh_tangent_space_calculation.pdf
105 */
106 
107 TangentSpace::TangentSpace()
108  : weightByAngle_(true), weightByArea_(false), weightByUVArea_(false),
109  weightByAngleGUI_(0), weightByAreaGUI_(0), weightByUVAreaGUI_(0),
110  overwriteVertexNormals_(true), overwriteNormalsGUI_(0),
111  preserveTextureSeams_(true), preserveTextureSeamsGUI_(0),
112  decompMethod_(DECOMP_HALF_ANGLE), decompMethodGUI_(0),
113  propName_("inTangent"), propNameGUI_(0)
114 {
115 }
116 
117 
118 TangentSpace::~TangentSpace()
119 {
120 }
121 
122 
123 
124 
128 void
130 {
131  QWidget* tool_ = new QWidget();
132  QSize size(100, 100);
133  tool_->resize(size);
134 
135  QVBoxLayout* layout = new QVBoxLayout(tool_);
136 
137  QPushButton* button = new QPushButton("Compute per vertex",tool_);
138  QPushButton* buttonH = new QPushButton("Compute per halfedge",tool_);
139 
140  weightByAngleGUI_ = new QCheckBox("Weight by angle", tool_);
141  weightByAngleGUI_->setChecked(weightByAngle_);
142  weightByAngleGUI_->setToolTip("inner angle at vertices");
143 
144  weightByAreaGUI_ = new QCheckBox("Weight by area", tool_);
145  weightByAreaGUI_->setChecked(weightByArea_);
146  weightByAreaGUI_->setToolTip("area of triangles in object-space");
147 
148  weightByUVAreaGUI_ = new QCheckBox("Weight by texture area", tool_);
149  weightByUVAreaGUI_->setChecked(weightByUVArea_);
150  weightByUVAreaGUI_->setToolTip("area of triangles in texture-space");
151 
152  decompMethodGUI_ = new QComboBox(tool_);
153 
154  decompMethodGUI_->addItem("Gram-Schmidt", DECOMP_GRAM_SCHMIDT);
155  decompMethodGUI_->addItem("Half angle", DECOMP_HALF_ANGLE);
156 #ifdef ENABLE_EIGEN3
157  decompMethodGUI_->addItem("Polar decomposition", DECOMP_POLAR);
158 #endif // ENABLE_EIGEN3
159  decompMethodGUI_->setCurrentIndex(decompMethod_);
160  decompMethodGUI_->setToolTip("Orthogonalization method");
161 
162  overwriteNormalsGUI_ = new QCheckBox("Overwrite normals", tool_);
163  overwriteNormalsGUI_->setChecked(overwriteVertexNormals_);
164  overwriteNormalsGUI_->setToolTip("Discard current normals of mesh and use results from tbn matrix");
165 
166  preserveTextureSeamsGUI_ = new QCheckBox("Preserve texture seams", tool_);
167  preserveTextureSeamsGUI_->setChecked(preserveTextureSeams_);
168  preserveTextureSeamsGUI_->setToolTip("Don't smooth along texture seams (only for per-halfedge tangents)");
169 
170  propNameGUI_ = new QLineEdit(tool_);
171  propNameGUI_->setText(propName_.c_str());
172  propNameGUI_->setToolTip("Property name");
173 
174  layout->addWidget(weightByAngleGUI_);
175  layout->addWidget(weightByAreaGUI_);
176  layout->addWidget(weightByUVAreaGUI_);
177  layout->addWidget(preserveTextureSeamsGUI_);
178  layout->addWidget(overwriteNormalsGUI_);
179  layout->addWidget(decompMethodGUI_);
180  layout->addWidget(propNameGUI_);
181  layout->addWidget(button);
182  layout->addWidget(buttonH);
183 
184  // connect signals->slots
185  connect(button,SIGNAL(clicked() ),this,SLOT(slotComputePerVertex()));
186  connect(buttonH,SIGNAL(clicked() ),this,SLOT(slotComputePerHalfedge()));
187 
188  QIcon* icon = new QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() + "tangent_space_icon.png");
189  emit addToolbox( tr("TangentSpace") , tool_ , icon );
190 
191 
192 }
193 
194 
195 void TangentSpace::getGUIConfig()
196 {
197  weightByAngle_ = weightByAngleGUI_->isChecked();
198  weightByArea_ = weightByAreaGUI_->isChecked();
199  weightByUVArea_ = weightByUVAreaGUI_->isChecked();
200  decompMethod_ = decompMethodGUI_->itemData( decompMethodGUI_->currentIndex() ).toInt();
201  overwriteVertexNormals_ = overwriteNormalsGUI_->isChecked();
202  preserveTextureSeams_ = preserveTextureSeamsGUI_->isChecked();
203  propName_ = propNameGUI_->text().toStdString();
204 }
205 
206 
207 void TangentSpace::slotComputePerVertex( )
208 {
209  // get configuration from gui elements
210  getGUIConfig();
211 
212  // process all tri-meshes
214  o_it != PluginFunctions::objectsEnd(); ++o_it)
215  {
217 
218  if ( object == 0 )
219  {
220  std::cerr << "No mesh!" << std::endl;
221  continue;
222  }
223 
224  // Get triangle mesh
225  TriMesh* mesh = PluginFunctions::triMesh(*o_it);
226 
227  if (mesh == 0)
228  {
229  std::cerr << "No mesh!" << std::endl;
230  return;
231  }
232 
233  if (!mesh->has_halfedge_texcoords2D() && !mesh->has_vertex_texcoords2D())
234  {
235  std::cerr << "No texcoords!" << std::endl;
236  return;
237  }
238 
239  computePerVertexTangents(mesh);
240 
241  emit updatedObject(o_it->id(),UPDATE_ALL);
242  }
243 }
244 
245 void TangentSpace::slotComputePerHalfedge( )
246 {
247  // get configuration from gui elements
248  getGUIConfig();
249 
250  // process all tri-meshes
252  o_it != PluginFunctions::objectsEnd(); ++o_it)
253  {
255 
256  if ( object == 0 )
257  {
258  std::cerr << "No mesh!" << std::endl;
259  continue;
260  }
261 
262  // Get triangle mesh
263  TriMesh* mesh = PluginFunctions::triMesh(*o_it);
264 
265  if (mesh == 0)
266  {
267  std::cerr << "No mesh!" << std::endl;
268  return;
269  }
270 
271  if (!mesh->has_halfedge_texcoords2D() && !mesh->has_vertex_texcoords2D())
272  {
273  std::cerr << "No texcoords!" << std::endl;
274  return;
275  }
276 
277  if (!mesh->has_halfedge_normals())
278  {
279  std::cerr << "No halfedge normals!" << std::endl;
280  return;
281  }
282 
283  computePerHalfedgeTangents(mesh);
284 
285  emit updatedObject(o_it->id(),UPDATE_ALL);
286  }
287 }
288 
289 void TangentSpace::computePerVertexTangents( TriMesh* mesh )
290 {
291  OpenMesh::VPropHandleT< ACG::Vec4f > vtangentPropHandle;
292 
293  if (!mesh->get_property_handle( vtangentPropHandle, propName_))
294  mesh->add_property( vtangentPropHandle, propName_ );
295 
296  const int numVertices = mesh->n_vertices();
297 
298  // computation can be parallel
299 #ifdef USE_OPENMP
300 #pragma omp parallel for
301 #endif
302  for ( int curVertex = 0; curVertex < numVertices; ++curVertex )
303  {
304  TriMesh::VertexHandle curVertexHandle = mesh->vertex_handle(curVertex);
305  ACG::Vec3f vertexNormal(0.0f, 0.0f, 0.0f);
306 
307  if ( mesh->has_vertex_normals() )
308  vertexNormal = ACG::Vec3f(mesh->normal(curVertexHandle)[0], mesh->normal(curVertexHandle)[1], mesh->normal(curVertexHandle)[2]);
309 
310  TangentBasis averageTBN;
311  averageTBN.setZero();
312 
313  for ( TriMesh::VertexIHalfedgeIter voh_it = mesh->vih_begin(curVertexHandle); voh_it != mesh->vih_end(curVertexHandle); ++voh_it )
314  {
315  if (!mesh->is_boundary(*voh_it))
316  {
317  TriMesh::FaceHandle fh = mesh->face_handle(*voh_it);
318 
319  TangentBasis triangleTBN;
320  computeWeightedTangentSpace(mesh, *voh_it, &triangleTBN);
321 
322  averageTBN.add(triangleTBN);
323  }
324  }
325 
326  if (!overwriteVertexNormals_)
327  {
328  // use precomputed normal instead of tangent space normal before orthogonalization
329  averageTBN.n = vertexNormal;
330  }
331 
332  averageTBN.orthonormalize(decompMethod_);
333 
334  // store tangent and parity
335  mesh->property(vtangentPropHandle, curVertexHandle) = ACG::Vec4f(averageTBN.t[0], averageTBN.t[1], averageTBN.t[2], averageTBN.parity);
336 // mesh->property(vtangentPropHandle, curVertexHandle) = ACG::Vec3d(averageTBN.t[0], averageTBN.t[1], averageTBN.t[2]);
337 
338  if (overwriteVertexNormals_ && mesh->has_vertex_normals())
339  mesh->set_normal(curVertexHandle, ACG::Vec3d(averageTBN.n[0], averageTBN.n[1], averageTBN.n[2]));
340  }
341 }
342 
343 // key for hashtable of smoothing groups
345 {
346  // normal
347  ACG::Vec3f n;
348  // texcoord
349  ACG::Vec2f uv;
350 
351  TangentSpace_SmoothingGroupKey(const ACG::Vec3f& _n, ACG::Vec2f _uv = ACG::Vec2f(0.0f, 0.0f))
352  : n(_n), uv(_uv)
353  {
354  }
355 
356  bool operator == (const TangentSpace_SmoothingGroupKey& rhs) const
357  {
358  float cosTheta = n | rhs.n;
359 
360  if (cosTheta > 0.998f)
361  {
362  ACG::Vec2f d = uv - rhs.uv;
363  if ( fabsf(d[0]) < cmp_eps && fabsf(d[1]) < cmp_eps )
364  return true;
365  }
366 
367  return false;
368  }
369 };
370 
371 uint qHash(const TangentSpace_SmoothingGroupKey& k)
372 {
373  // compute hash of each float
374  uint x[5];
375 
376  for (int i = 0; i < 5; ++i)
377  {
378  uint u;
379 
380  // get float from normal/uv, interpret as uint
381  if (i < 3)
382  u = *reinterpret_cast<const uint*>(k.n.data() + i);
383  else
384  u = *reinterpret_cast<const uint*>(k.uv.data() + i - 3);
385 
386  // discard high precision mantissa bits
387  u &= 0xfffffe00;
388 
389  x[i] = ::qHash(u);
390  }
391 
392  // combine hash values with prime numbers
393  uint p[5] = {439, 3373, 1033, 6673, 53};
394 
395  uint result = 0;
396 
397  for (int i = 0; i < 5; ++i)
398  result += x[i] * p[i];
399 
400  return ((result >> 22) ^ (result >> 11) ^ result);
401 }
402 
403 void TangentSpace::computePerHalfedgeTangents( TriMesh* mesh )
404 {
405  /*
406  tangent smoothing strategies:
407  - no smoothing across different halfedge normals
408  based on the assumption that normals have been computed via smoothing groups already
409 
410  - no smoothing across texture seams (parity check)
411 
412  implementation via smoothing groups:
413  */
414 
415  OpenMesh::HPropHandleT< ACG::Vec4f > htangentPropHandle;
416 
417  if (!mesh->get_property_handle( htangentPropHandle, propName_))
418  mesh->add_property( htangentPropHandle, propName_ );
419 
420  const int numVertices = mesh->n_vertices();
421 
422  // computation can be parallel
423 #ifdef USE_OPENMP
424 #pragma omp parallel for
425 #endif
426  for (int curVertex = 0; curVertex < numVertices; ++curVertex )
427  {
428  TriMesh::VertexHandle curVertexHandle = mesh->vertex_handle(curVertex);
429 
430  // map from normal,texc combination to first halfedge for that combination
431  QHash<TangentSpace_SmoothingGroupKey, TriMesh::HalfedgeHandle> UniqueNormals;
432 
433  // maps from halfedge -> smoothing group
434  std::map<TriMesh::HalfedgeHandle, int> SmoothingGroups;
435  int numNormalGroups = 0;
436 
437  for ( TriMesh::VertexIHalfedgeIter voh_it = mesh->vih_begin(curVertexHandle); voh_it != mesh->vih_end(curVertexHandle); ++voh_it )
438  {
439  // openmesh stores halfedges at boundaries, which do not belong to any face
440  // opposite halfedge should belong to a face, so its fine to skip empty halfedges
441  if ( mesh->is_boundary(*voh_it) )
442  continue;
443 
444  // halfedge normal
445  ACG::Vec3f hn = ACG::Vec3f(mesh->normal(*voh_it)[0], mesh->normal(*voh_it)[1], mesh->normal(*voh_it)[2]);
446 
447  // halfedge texcoord
448  ACG::Vec2f ht(0.0f, 0.0f);
449 
450  if (preserveTextureSeams_)
451  {
452  if (mesh->has_halfedge_texcoords2D())
453  ht = mesh->texcoord2D(*voh_it);
454  else
455  ht = mesh->texcoord2D( mesh->to_vertex_handle(*voh_it) );
456  }
457 
458  TangentSpace_SmoothingGroupKey key(hn, ht);
459 
460  QHash<TangentSpace_SmoothingGroupKey, TriMesh::HalfedgeHandle>::iterator itNormalGroup = UniqueNormals.find(key);
461 
462  if (itNormalGroup == UniqueNormals.end())
463  {
464  // normal has not been encountered yet -> new smoothing group
465  SmoothingGroups[*voh_it] = numNormalGroups++;
466  UniqueNormals[key] = *voh_it;
467  }
468  else
469  {
470  // normal has been references already, use same smoothing group
471  TriMesh::HalfedgeHandle firstHalfedge = *itNormalGroup;
472  SmoothingGroups[*voh_it] = SmoothingGroups[firstHalfedge];
473  }
474  }
475 
476  // tangent space per smoothing group
477  // smoothing group with flipped parity at index: smGroup + numNormalGroups
478  std::vector<TangentBasis> TangentSpaces(numNormalGroups * 2);
479 
480  // set zero
481  for (size_t i = 0; i < TangentSpaces.size(); ++i)
482  TangentSpaces[i].setZero();
483 
484  int numSmoothingGroups = numNormalGroups;
485 
486  for ( TriMesh::VertexIHalfedgeIter voh_it = mesh->vih_begin(curVertexHandle); voh_it != mesh->vih_end(curVertexHandle); ++voh_it )
487  {
488  // skip empty boundary halfedge
489  if (mesh->is_boundary(*voh_it))
490  continue;
491 
492  TriMesh::FaceHandle fh = mesh->face_handle(*voh_it);
493 
494  // get halfedge normal
495  ACG::Vec3f hn = ACG::Vec3f(mesh->normal(*voh_it)[0], mesh->normal(*voh_it)[1], mesh->normal(*voh_it)[2]);
496 
497  // get smoothing group of current halfedge
498  int smGroup = SmoothingGroups[*voh_it];
499 
500  if (fh == mesh->InvalidFaceHandle)
501  {
502  std::cerr << "warning: face_handle invalid for halfedge " << *voh_it << std::endl;
503  continue;
504  }
505 
506  TangentBasis triTbn;
507  computeWeightedTangentSpace(mesh, *voh_it, &triTbn);
508 
509  float parity = triTbn.parity;
510  float groupParity = TangentSpaces[smGroup].parity;
511 
512 
513  if (groupParity == 0.0f || groupParity == parity)
514  {
515  // compute weighted average inside smoothing group
516  TangentSpaces[smGroup].add(triTbn);
517  TangentSpaces[smGroup].parity = parity;
518  }
519  else
520  {
521  // mirrored uv coords
522  // create new smoothing group along seam
523 
524  // group id for flipped parity = groupId + numNormalGroups
525  smGroup += numNormalGroups;
526  SmoothingGroups[*voh_it] = smGroup;
527 
528  // make sure flipped parity is correct
529  if (TangentSpaces[smGroup].parity == 0.0f)
530  {
531  ++numSmoothingGroups;
532  TangentSpaces[smGroup].parity = triTbn.parity;
533  }
534  else
535  assert(TangentSpaces[smGroup].parity == triTbn.parity);
536 
537  TangentSpaces[smGroup].add(triTbn);
538  }
539 
540  // use precomputed halfedge normal instead of tangent space normal
541  TangentSpaces[smGroup].n = hn;
542  }
543 
544 
545  // orthonormalize
546  int numTBNMatrices = 0;
547 
548  for (size_t i = 0; i < TangentSpaces.size(); ++i)
549  {
550  TangentBasis* tbn = &TangentSpaces[i];
551 
552  if (tbn->parity != 0.0f)
553  {
554  tbn->orthonormalize(decompMethod_);
555  ++numTBNMatrices;
556  }
557  }
558 
559  if (!numTBNMatrices)
560  {
561  std::cerr << "error: could not compute halfedge tangents for vertex " << curVertexHandle << std::endl;
562  }
563  else
564  {
565  if (numTBNMatrices != numSmoothingGroups)
566  std::cerr << "warning: could not compute tangents for all smoothing groups of vertex" << curVertexHandle << std::endl;
567 
568  // set per halfedge property
569 
570  for ( TriMesh::VertexIHalfedgeIter voh_it = mesh->vih_begin(curVertexHandle); voh_it != mesh->vih_end(curVertexHandle); ++voh_it )
571  {
572  // get matrix in smoothing group of halfedge
573  int smGroup = SmoothingGroups[*voh_it];
574  TangentBasis* tbn = &TangentSpaces[smGroup];
575 
576  if (tbn->parity == 0.0f)
577  {
578  // something went wrong, choose matrix from other smoothing group
579 
580  tbn = &TangentSpaces[0];
581 
582  while (tbn->parity == 0.0f)
583  ++tbn;
584  }
585 
586  // update normals after polar decomp
587  if (decompMethod_ == DECOMP_POLAR)
588  {
589  if (overwriteVertexNormals_)
590  mesh->set_normal(*voh_it, ACG::Vec3d(tbn->n[0], tbn->n[1], tbn->n[2]));
591  else
592  {
593  // normal may not be changed, rotate from back to normal
594  ACG::Vec3f storedNormal = ACG::Vec3f(mesh->normal(*voh_it)[0], mesh->normal(*voh_it)[1], mesh->normal(*voh_it)[2]);
595 
596  if (tbn->n != storedNormal)
597  {
598  tbn->n = storedNormal;
599  tbn->orthonormalize(DECOMP_HALF_ANGLE);
600  }
601  }
602  }
603 
604  mesh->property(htangentPropHandle, *voh_it) = ACG::Vec4f(tbn->t[0], tbn->t[1], tbn->t[2], tbn->parity);
605 // mesh->property(htangentPropHandle, *voh_it) = ACG::Vec3d(tbn->t[0], tbn->t[1], tbn->t[2]);
606  }
607  }
608 
609  }
610 }
611 
612 
613 float TangentSpace::computeTriTBN( const ACG::Vec3f* _pos, const ACG::Vec2f* _texc, ACG::Vec3f* _outT, ACG::Vec3f* _outB, ACG::Vec3f* _outN, bool _divByDet )
614 {
615  ACG::Vec3f q1 = _pos[1] - _pos[0];
616  ACG::Vec3f q2 = _pos[2] - _pos[0];
617 
618  ACG::Vec2f r1 = _texc[1] - _texc[0];
619  ACG::Vec2f r2 = _texc[2] - _texc[0];
620 
621  // solve q = r * TB
622 
623  // invert 2x2 matrix r
624  float det = r1[0]*r2[1] - r2[0]*r1[1];
625 
626  float detInv = 0.0f;
627 
628  if (_divByDet)
629  {
630  if (fabsf(det) < 1e-6f)
631  detInv = 1.0f / (det + 1e-5f);
632  else
633  detInv = 1.0f / det;
634  }
635  else
636  {
637  // if the tbn matrix gets normalized anyway there is no need to divide by the determinant
638  // -> more robust
639  detInv = det >= 0.0f ? 1.0f : -1.0f;
640  }
641 
642  // TB = inv(r) * q
643 
644  ACG::Vec3f t = (q1 * r2[1] - q2 * r1[1]) * detInv;
645  ACG::Vec3f b = (q2 * r1[0] - q1 * r2[0]) * detInv;
646 // ACG::Vec3f n = t % b; // % = cross product
647  ACG::Vec3f n = q1 % q2; // gives better results than cross(t,b)
648 
649  if (_outT) *_outT = t;
650  if (_outB) *_outB = b;
651  if (_outN) *_outN = n;
652 
653  return computeParity(t,b,n);
654 }
655 
656 float TangentSpace::computeTriTBN( TriMesh* mesh, TriMesh::FaceHandle _fh, ACG::Vec3f* _outT, ACG::Vec3f* _outB, ACG::Vec3f* _outN, bool _divByDet )
657 {
658  ACG::Vec3f pos[3]; // position
659  ACG::Vec2f texc[3]; // uv texcoord
660  ACG::Vec3f t,b,n; // tangent space matrix
661 
662  // get pos, texc array of triangle
663  int triV = 0;
664  for (TriMesh::FaceHalfedgeIter fh_it = mesh->fh_begin(_fh) ; fh_it != mesh->fh_end(_fh); ++fh_it)
665  {
666  TriMesh::HalfedgeHandle hh = *fh_it;
667  TriMesh::VertexHandle vh = mesh->to_vertex_handle(hh);
668  pos[triV] = mesh->point(vh);
669 
670  if (mesh->has_halfedge_texcoords2D())
671  texc[triV] = mesh->texcoord2D(hh);
672  else
673  texc[triV] = mesh->texcoord2D(vh);
674 
675  ++triV;
676  }
677 
678  // compute unnormalized tangent space
679  return computeTriTBN(pos, texc, _outT, _outB, _outN, _divByDet);
680 }
681 
682 float TangentSpace::computeFaceTBN( PolyMesh* mesh, PolyMesh::FaceHandle _fh, ACG::Vec3f* _outT, ACG::Vec3f* _outB, ACG::Vec3f* _outN, bool _divByDet)
683 {
684  // tangent space matrix is well defined for a face if
685  // - planar polygon
686  // - linear uv field (ie. constant uv gradient inside polygon)
687  // faces of TriMesh always satisfy these conditions
688 
689  ACG::Vec3f pos[3]; // position
690  ACG::Vec2f texc[3]; // uv texcoord
691  ACG::Vec3f t,b,n; // tangent space matrix
692 
693  // get pos, texc array of triangle
694  int triV = 0;
695  for (PolyMesh::FaceHalfedgeIter fh_it = mesh->fh_begin(_fh) ; fh_it != mesh->fh_end(_fh) && triV < 3; ++fh_it)
696  {
697  PolyMesh::HalfedgeHandle hh = *fh_it;
698  PolyMesh::VertexHandle vh = mesh->to_vertex_handle(hh);
699  pos[triV] = mesh->point(vh);
700 
701  if (mesh->has_halfedge_texcoords2D())
702  texc[triV] = mesh->texcoord2D(hh);
703  else
704  texc[triV] = mesh->texcoord2D(vh);
705 
706  ++triV;
707  }
708 
709  // compute unnormalized tangent space
710  return computeTriTBN(pos, texc, _outT, _outB, _outN, _divByDet);
711 }
712 
713 
714 
715 float TangentSpace::computeUVArea( TriMesh* _mesh, TriMesh::HalfedgeHandle _h )
716 {
717  if (_mesh->is_boundary(_h))
718  return 0.0f;
719 
720  TriMesh::FaceHandle fh = _mesh->face_handle(_h);
721 
722  ACG::Vec2f texc[3]; // uv texcoord
723 
724  // get texcoords of tri
725  int triV = 0;
726  for (TriMesh::FaceHalfedgeIter fh_it = _mesh->fh_begin(fh) ; fh_it != _mesh->fh_end(fh); ++fh_it)
727  {
728  TriMesh::HalfedgeHandle hh = *fh_it;
729  TriMesh::VertexHandle vh = _mesh->to_vertex_handle(hh);
730  if (_mesh->has_halfedge_texcoords2D())
731  texc[triV] = _mesh->texcoord2D(hh);
732  else
733  texc[triV] = _mesh->texcoord2D(vh);
734 
735  ++triV;
736  }
737 
738  ACG::Vec2f q1 = texc[1] - texc[0];
739  ACG::Vec2f q2 = texc[2] - texc[0];
740 
741  // area = det(q1, q2) / 2
742  float area = fabsf( q1[0]*q2[1] - q1[1]*q2[0] ) * 0.5f;
743 
744  return area;
745 }
746 
747 
748 
749 void TangentSpace::computeWeightedTangentSpace( TriMesh* mesh, TriMesh::HalfedgeHandle _h, TangentBasis* _out )
750 {
751  _out->setZero();
752  if (mesh->is_boundary(_h))
753  return;
754 
755  TriMesh::FaceHandle fh = mesh->face_handle(_h);
756 
757  bool weighted = weightByAngle_ || weightByArea_;
758  _out->parity = computeTriTBN(mesh, fh, &_out->t, &_out->b, &_out->n, !weighted);
759 
760  if (weighted)
761  {
762  _out->t.normalize();
763  _out->b.normalize();
764  _out->n.normalize();
765  }
766 
767  float weight = 1.0f;
768 
769  // weight by inner angle to get tessellation independent tangents
770  if (weightByAngle_)
771  weight *= mesh->calc_sector_angle(_h);
772 
773  // weight by area to prefer bigger faces over small faces
774  if (weightByArea_)
775  weight *= mesh->calc_sector_area(_h); // angle and area equally weighted
776 
777  if (weightByUVArea_)
778  weight *= computeUVArea(mesh, _h);
779 
780 // if (weight == 0.0f)
781 // weight = 1.0f; // no weighting
782 
783  _out->t *= weight;
784  _out->b *= weight;
785  _out->n *= weight;
786 }
787 
788 
789 void TangentSpace::computeWeightedTangentSpace( PolyMesh* mesh, PolyMesh::HalfedgeHandle _h, TangentBasis* _out )
790 {
791  _out->setZero();
792  if (mesh->is_boundary(_h))
793  return;
794 
795  PolyMesh::FaceHandle fh = mesh->face_handle(_h);
796 
797  bool weighted = weightByAngle_ || weightByArea_;
798  _out->parity = computeFaceTBN(mesh, fh, &_out->t, &_out->b, &_out->n, !weighted);
799 
800  if (weighted)
801  {
802  _out->t.normalize();
803  _out->b.normalize();
804  _out->n.normalize();
805  }
806 
807  float weight = 1.0f;
808 
809  // weight by inner angle to get tessellation independent tangents
810  if (weightByAngle_)
811  weight *= mesh->calc_sector_angle(_h);
812 
813  // weight by area to prefer bigger faces over small faces
814  if (weightByArea_)
815  weight *= mesh->calc_sector_area(_h); // angle and area equally weighted
816 
817 // if (weight == 0.0f)
818 // weight = 1.0f; // no weighting
819 
820  _out->t *= weight;
821  _out->b *= weight;
822  _out->n *= weight;
823 }
824 
825 
826 float TangentSpace::computeParity( const ACG::Vec3f& t, const ACG::Vec3f& b, const ACG::Vec3f& n )
827 {
828  return ((b | (n % t)) < 0.0f) ? -1.0f : 1.0f; // | = dot, % = cross
829 }
830 
831 void TangentSpace::TangentBasis::computeParity()
832 {
833  parity = ((b | (n % t)) < 0.0f) ? -1.0f : 1.0f; // | = dot, % = cross
834 }
835 
836 void TangentSpace::TangentBasis::orthonormalize(int method)
837 {
838  normalize();
839 
840  // check for linear independence
841  if ( fabsf( fabsf(t | b) - 1.0f ) < cmp_eps )
842  {
843  std::cerr << "warning: degenerated uv mapping" << std::endl;
844  b = n % t;
845  }
846 
847  if ( fabsf( fabsf(n | t) - 1.0f ) < cmp_eps )
848  {
849  std::cerr << "warning: degenerated uv mapping" << std::endl;
850  t = b % n;
851  }
852 
853  // check parity after orthogonalization with current parity
854  computeParity();
855  const float prevParity = parity;
856 
857 
858 
859  // extract orthogonal matrix via given method
860 
861  if (method == TangentSpace::DECOMP_GRAM_SCHMIDT)
862  {
863  // Gram-Schmidt orthogonalization weights: n > t > b
864 
865  t = t - (n | t) * n;
866  t.normalize();
867 
868  b = (n % t) * parity;
869  }
870  else if (method == TangentSpace::DECOMP_HALF_ANGLE)
871  {
872  // Gram-Schmidt orthogonalization weights: n > t = b
873 
874  // make t and b orthogonal to n
875 
876  t = t - (n | t) * n;
877  t.normalize();
878 
879  b = b - (n | b) * n;
880  b.normalize();
881 
882 
883  // make t and b orthogonal to each other with equal weighting
884 
885  // half vector between t and b
886  ACG::Vec3f h = t + b;
887  h.normalize();
888 
889  // vector orthogonal to h and n
890  ACG::Vec3f tH = h % n;
891 
892  tH.normalize();
893 
894  // new tangent is half vector between h and tH
895  // this is the same as rotating h by 45 deg in the direction of t
896 
897  float handedness = (tH | t) > 0.0f ? 1.0f : -1.0f;
898 
899  t = h + tH * handedness;
900  t.normalize();
901 
902 
903 
904  b = (n % t) * parity;
905  }
906  else if (method == TangentSpace::DECOMP_POLAR)
907  {
908 #ifdef ENABLE_EIGEN3
909 
910  // polar decomposition, t,b,n all equally weighted
911 
912  // M = R * H, where R is orthogonal
913  // given svd M = U * S * V, the polar decomposition of M is given as
914  // R = U * transpose(V)
915  // H = V * S * transpose(V)
916 
917  // M = tangent space matrix
918  Eigen::Matrix3f M;
919 
920  for (int r = 0; r < 3; ++r)
921  {
922  M(r, 0) = t[r];
923  M(r, 1) = b[r];
924  M(r, 2) = n[r];
925  }
926 
927  Eigen::JacobiSVD<Eigen::Matrix3f, Eigen::FullPivHouseholderQRPreconditioner> svd(M, Eigen::ComputeFullU | Eigen::ComputeFullV);
928 
929  Eigen::Matrix3f R = svd.matrixU() * svd.matrixV().transpose();
930 
931  for (int r = 0; r < 3; ++r)
932  {
933  t[r] = R(r, 0);
934  b[r] = R(r, 1);
935  n[r] = R(r, 2);
936  }
937 
938  // check result
939 // assert( fabsf(t | b) < 1e-6f );
940 // assert( fabsf(t | n) < 1e-6f );
941 // assert( fabsf(n | b) < 1e-6f );
942 //
943 // assert( fabsf(t.norm() - 1.0f) < 1e-6f);
944 // assert( fabsf(b.norm() - 1.0f) < 1e-6f);
945 // assert( fabsf(n.norm() - 1.0f) < 1e-6f);
946 //
947 // computeParity();
948 // assert(prevParity == parity);
949 
950 #else
951  // use qr decomposition with equal tangent weighting instead
952  orthonormalize(TangentSpace::DECOMP_HALF_ANGLE);
953 #endif // ENABLE_EIGEN3
954  }
955 
956  // parity check
957  computeParity();
958  if (parity != prevParity)
959  {
960  // parity flip indicates degenerated triangle or degenerated uv parameterization
961  std::cerr << "warning: parity flip after orthogonalization of tbn matrix" << std::endl;
962  }
963 }
964 
965 void TangentSpace::TangentBasis::setZero()
966 {
967  t = b = n = ACG::Vec3f(0.0f, 0.0f, 0.0f);
968  parity = 0.0f;
969 }
970 
971 void TangentSpace::TangentBasis::normalize()
972 {
973  t.normalize();
974  b.normalize();
975  n.normalize();
976 }
977 
978 void TangentSpace::TangentBasis::add( const TangentBasis& _r )
979 {
980  t += _r.t;
981  b += _r.b;
982  n += _r.n;
983 }
984 
985 
986 #if QT_VERSION < 0x050000
987  Q_EXPORT_PLUGIN2( tangentspaceplugin , TangentSpace );
988 #endif
TriMesh * triMesh(BaseObjectData *_object)
Get a triangle mesh from an object.
Type for a MeshObject containing a triangle mesh.
Definition: TriangleMesh.hh:73
void pluginsInitialized()
Set the scripting slot descriptions.
VectorT< float, 3 > Vec3f
Definition: VectorT.hh:125
Kernel::FaceHalfedgeIter FaceHalfedgeIter
Circulator.
Definition: PolyMeshT.hh:171
#define DATA_TRIANGLE_MESH
Definition: TriangleMesh.hh:66
TriMeshObject * triMeshObject(BaseObjectData *_object)
Cast an BaseObject to a TriMeshObject if possible.
VectorT< float, 4 > Vec4f
Definition: VectorT.hh:144
const UpdateType UPDATE_ALL(UpdateTypeSet(1))
Identifier for all updates.
Kernel::VertexIHalfedgeIter VertexIHalfedgeIter
Circulator.
Definition: PolyMeshT.hh:167
DLLEXPORT ObjectIterator objectsEnd()
Return Iterator to Object End.
const QStringList TARGET_OBJECTS("target")
Iterable object range.
Kernel::VertexHandle VertexHandle
Handle for referencing the corresponding item.
Definition: PolyMeshT.hh:139