46 #include "TangentSpace.hh"
51 #if QT_VERSION >= 0x050000
56 #include <QVBoxLayout>
69 #endif // ENABLE_EIGEN3
74 const float cmp_eps = std::numeric_limits<float>::epsilon();
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)
118 TangentSpace::~TangentSpace()
131 QWidget* tool_ =
new QWidget();
132 QSize size(100, 100);
135 QVBoxLayout* layout =
new QVBoxLayout(tool_);
137 QPushButton* button =
new QPushButton(
"Compute per vertex",tool_);
138 QPushButton* buttonH =
new QPushButton(
"Compute per halfedge",tool_);
140 weightByAngleGUI_ =
new QCheckBox(
"Weight by angle", tool_);
141 weightByAngleGUI_->setChecked(weightByAngle_);
142 weightByAngleGUI_->setToolTip(
"inner angle at vertices");
144 weightByAreaGUI_ =
new QCheckBox(
"Weight by area", tool_);
145 weightByAreaGUI_->setChecked(weightByArea_);
146 weightByAreaGUI_->setToolTip(
"area of triangles in object-space");
148 weightByUVAreaGUI_ =
new QCheckBox(
"Weight by texture area", tool_);
149 weightByUVAreaGUI_->setChecked(weightByUVArea_);
150 weightByUVAreaGUI_->setToolTip(
"area of triangles in texture-space");
152 decompMethodGUI_ =
new QComboBox(tool_);
154 decompMethodGUI_->addItem(
"Gram-Schmidt", DECOMP_GRAM_SCHMIDT);
155 decompMethodGUI_->addItem(
"Half angle", DECOMP_HALF_ANGLE);
157 decompMethodGUI_->addItem(
"Polar decomposition", DECOMP_POLAR);
158 #endif // ENABLE_EIGEN3
159 decompMethodGUI_->setCurrentIndex(decompMethod_);
160 decompMethodGUI_->setToolTip(
"Orthogonalization method");
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");
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)");
170 propNameGUI_ =
new QLineEdit(tool_);
171 propNameGUI_->setText(propName_.c_str());
172 propNameGUI_->setToolTip(
"Property name");
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);
185 connect(button,SIGNAL(clicked() ),
this,SLOT(slotComputePerVertex()));
186 connect(buttonH,SIGNAL(clicked() ),
this,SLOT(slotComputePerHalfedge()));
188 QIcon* icon =
new QIcon(OpenFlipper::Options::iconDirStr() + OpenFlipper::Options::dirSeparator() +
"tangent_space_icon.png");
189 emit addToolbox( tr(
"TangentSpace") , tool_ , icon );
195 void TangentSpace::getGUIConfig()
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();
207 void TangentSpace::slotComputePerVertex( )
220 std::cerr <<
"No mesh!" << std::endl;
229 std::cerr <<
"No mesh!" << std::endl;
233 if (!mesh->has_halfedge_texcoords2D() && !mesh->has_vertex_texcoords2D())
235 std::cerr <<
"No texcoords!" << std::endl;
239 computePerVertexTangents(mesh);
245 void TangentSpace::slotComputePerHalfedge( )
258 std::cerr <<
"No mesh!" << std::endl;
267 std::cerr <<
"No mesh!" << std::endl;
271 if (!mesh->has_halfedge_texcoords2D() && !mesh->has_vertex_texcoords2D())
273 std::cerr <<
"No texcoords!" << std::endl;
277 if (!mesh->has_halfedge_normals())
279 std::cerr <<
"No halfedge normals!" << std::endl;
283 computePerHalfedgeTangents(mesh);
289 void TangentSpace::computePerVertexTangents( TriMesh* mesh )
293 if (!mesh->get_property_handle( vtangentPropHandle, propName_))
294 mesh->add_property( vtangentPropHandle, propName_ );
296 const int numVertices = mesh->n_vertices();
300 #pragma omp parallel for
302 for (
int curVertex = 0; curVertex < numVertices; ++curVertex )
307 if ( mesh->has_vertex_normals() )
308 vertexNormal =
ACG::Vec3f(mesh->normal(curVertexHandle)[0], mesh->normal(curVertexHandle)[1], mesh->normal(curVertexHandle)[2]);
310 TangentBasis averageTBN;
311 averageTBN.setZero();
315 if (!mesh->is_boundary(*voh_it))
317 TriMesh::FaceHandle fh = mesh->face_handle(*voh_it);
319 TangentBasis triangleTBN;
320 computeWeightedTangentSpace(mesh, *voh_it, &triangleTBN);
322 averageTBN.add(triangleTBN);
326 if (!overwriteVertexNormals_)
329 averageTBN.n = vertexNormal;
332 averageTBN.orthonormalize(decompMethod_);
335 mesh->property(vtangentPropHandle, curVertexHandle) =
ACG::Vec4f(averageTBN.t[0], averageTBN.t[1], averageTBN.t[2], averageTBN.parity);
338 if (overwriteVertexNormals_ && mesh->has_vertex_normals())
339 mesh->set_normal(curVertexHandle,
ACG::Vec3d(averageTBN.n[0], averageTBN.n[1], averageTBN.n[2]));
358 float cosTheta = n | rhs.n;
360 if (cosTheta > 0.998f)
363 if ( fabsf(d[0]) < cmp_eps && fabsf(d[1]) < cmp_eps )
376 for (
int i = 0; i < 5; ++i)
382 u = *
reinterpret_cast<const uint*
>(k.n.data() + i);
384 u = *
reinterpret_cast<const uint*
>(k.uv.data() + i - 3);
393 uint p[5] = {439, 3373, 1033, 6673, 53};
397 for (
int i = 0; i < 5; ++i)
398 result += x[i] * p[i];
400 return ((result >> 22) ^ (result >> 11) ^ result);
403 void TangentSpace::computePerHalfedgeTangents( TriMesh* mesh )
417 if (!mesh->get_property_handle( htangentPropHandle, propName_))
418 mesh->add_property( htangentPropHandle, propName_ );
420 const int numVertices = mesh->n_vertices();
424 #pragma omp parallel for
426 for (
int curVertex = 0; curVertex < numVertices; ++curVertex )
431 QHash<TangentSpace_SmoothingGroupKey, TriMesh::HalfedgeHandle> UniqueNormals;
434 std::map<TriMesh::HalfedgeHandle, int> SmoothingGroups;
435 int numNormalGroups = 0;
441 if ( mesh->is_boundary(*voh_it) )
445 ACG::Vec3f hn =
ACG::Vec3f(mesh->normal(*voh_it)[0], mesh->normal(*voh_it)[1], mesh->normal(*voh_it)[2]);
450 if (preserveTextureSeams_)
452 if (mesh->has_halfedge_texcoords2D())
453 ht = mesh->texcoord2D(*voh_it);
455 ht = mesh->texcoord2D( mesh->to_vertex_handle(*voh_it) );
460 QHash<TangentSpace_SmoothingGroupKey, TriMesh::HalfedgeHandle>::iterator itNormalGroup = UniqueNormals.find(key);
462 if (itNormalGroup == UniqueNormals.end())
465 SmoothingGroups[*voh_it] = numNormalGroups++;
466 UniqueNormals[key] = *voh_it;
471 TriMesh::HalfedgeHandle firstHalfedge = *itNormalGroup;
472 SmoothingGroups[*voh_it] = SmoothingGroups[firstHalfedge];
478 std::vector<TangentBasis> TangentSpaces(numNormalGroups * 2);
481 for (
size_t i = 0; i < TangentSpaces.size(); ++i)
482 TangentSpaces[i].setZero();
484 int numSmoothingGroups = numNormalGroups;
489 if (mesh->is_boundary(*voh_it))
492 TriMesh::FaceHandle fh = mesh->face_handle(*voh_it);
495 ACG::Vec3f hn =
ACG::Vec3f(mesh->normal(*voh_it)[0], mesh->normal(*voh_it)[1], mesh->normal(*voh_it)[2]);
498 int smGroup = SmoothingGroups[*voh_it];
500 if (fh == mesh->InvalidFaceHandle)
502 std::cerr <<
"warning: face_handle invalid for halfedge " << *voh_it << std::endl;
507 computeWeightedTangentSpace(mesh, *voh_it, &triTbn);
509 float parity = triTbn.parity;
510 float groupParity = TangentSpaces[smGroup].parity;
513 if (groupParity == 0.0f || groupParity == parity)
516 TangentSpaces[smGroup].add(triTbn);
517 TangentSpaces[smGroup].parity = parity;
525 smGroup += numNormalGroups;
526 SmoothingGroups[*voh_it] = smGroup;
529 if (TangentSpaces[smGroup].parity == 0.0f)
531 ++numSmoothingGroups;
532 TangentSpaces[smGroup].parity = triTbn.parity;
535 assert(TangentSpaces[smGroup].parity == triTbn.parity);
537 TangentSpaces[smGroup].add(triTbn);
541 TangentSpaces[smGroup].n = hn;
546 int numTBNMatrices = 0;
548 for (
size_t i = 0; i < TangentSpaces.size(); ++i)
550 TangentBasis* tbn = &TangentSpaces[i];
552 if (tbn->parity != 0.0f)
554 tbn->orthonormalize(decompMethod_);
561 std::cerr <<
"error: could not compute halfedge tangents for vertex " << curVertexHandle << std::endl;
565 if (numTBNMatrices != numSmoothingGroups)
566 std::cerr <<
"warning: could not compute tangents for all smoothing groups of vertex" << curVertexHandle << std::endl;
573 int smGroup = SmoothingGroups[*voh_it];
574 TangentBasis* tbn = &TangentSpaces[smGroup];
576 if (tbn->parity == 0.0f)
580 tbn = &TangentSpaces[0];
582 while (tbn->parity == 0.0f)
587 if (decompMethod_ == DECOMP_POLAR)
589 if (overwriteVertexNormals_)
590 mesh->set_normal(*voh_it,
ACG::Vec3d(tbn->n[0], tbn->n[1], tbn->n[2]));
594 ACG::Vec3f storedNormal =
ACG::Vec3f(mesh->normal(*voh_it)[0], mesh->normal(*voh_it)[1], mesh->normal(*voh_it)[2]);
596 if (tbn->n != storedNormal)
598 tbn->n = storedNormal;
599 tbn->orthonormalize(DECOMP_HALF_ANGLE);
604 mesh->property(htangentPropHandle, *voh_it) =
ACG::Vec4f(tbn->t[0], tbn->t[1], tbn->t[2], tbn->parity);
624 float det = r1[0]*r2[1] - r2[0]*r1[1];
630 if (fabsf(det) < 1e-6f)
631 detInv = 1.0f / (det + 1e-5f);
639 detInv = det >= 0.0f ? 1.0f : -1.0f;
644 ACG::Vec3f t = (q1 * r2[1] - q2 * r1[1]) * detInv;
645 ACG::Vec3f b = (q2 * r1[0] - q1 * r2[0]) * detInv;
649 if (_outT) *_outT = t;
650 if (_outB) *_outB = b;
651 if (_outN) *_outN = n;
653 return computeParity(t,b,n);
656 float TangentSpace::computeTriTBN( TriMesh* mesh, TriMesh::FaceHandle _fh,
ACG::Vec3f* _outT,
ACG::Vec3f* _outB,
ACG::Vec3f* _outN,
bool _divByDet )
666 TriMesh::HalfedgeHandle hh = *fh_it;
668 pos[triV] = mesh->point(vh);
670 if (mesh->has_halfedge_texcoords2D())
671 texc[triV] = mesh->texcoord2D(hh);
673 texc[triV] = mesh->texcoord2D(vh);
679 return computeTriTBN(pos, texc, _outT, _outB, _outN, _divByDet);
682 float TangentSpace::computeFaceTBN( PolyMesh* mesh, PolyMesh::FaceHandle _fh,
ACG::Vec3f* _outT,
ACG::Vec3f* _outB,
ACG::Vec3f* _outN,
bool _divByDet)
697 PolyMesh::HalfedgeHandle hh = *fh_it;
698 PolyMesh::VertexHandle vh = mesh->to_vertex_handle(hh);
699 pos[triV] = mesh->point(vh);
701 if (mesh->has_halfedge_texcoords2D())
702 texc[triV] = mesh->texcoord2D(hh);
704 texc[triV] = mesh->texcoord2D(vh);
710 return computeTriTBN(pos, texc, _outT, _outB, _outN, _divByDet);
715 float TangentSpace::computeUVArea( TriMesh* _mesh, TriMesh::HalfedgeHandle _h )
717 if (_mesh->is_boundary(_h))
720 TriMesh::FaceHandle fh = _mesh->face_handle(_h);
728 TriMesh::HalfedgeHandle hh = *fh_it;
730 if (_mesh->has_halfedge_texcoords2D())
731 texc[triV] = _mesh->texcoord2D(hh);
733 texc[triV] = _mesh->texcoord2D(vh);
742 float area = fabsf( q1[0]*q2[1] - q1[1]*q2[0] ) * 0.5f;
749 void TangentSpace::computeWeightedTangentSpace( TriMesh* mesh, TriMesh::HalfedgeHandle _h, TangentBasis* _out )
752 if (mesh->is_boundary(_h))
755 TriMesh::FaceHandle fh = mesh->face_handle(_h);
757 bool weighted = weightByAngle_ || weightByArea_;
758 _out->parity = computeTriTBN(mesh, fh, &_out->t, &_out->b, &_out->n, !weighted);
771 weight *= mesh->calc_sector_angle(_h);
775 weight *= mesh->calc_sector_area(_h);
778 weight *= computeUVArea(mesh, _h);
789 void TangentSpace::computeWeightedTangentSpace( PolyMesh* mesh, PolyMesh::HalfedgeHandle _h, TangentBasis* _out )
792 if (mesh->is_boundary(_h))
795 PolyMesh::FaceHandle fh = mesh->face_handle(_h);
797 bool weighted = weightByAngle_ || weightByArea_;
798 _out->parity = computeFaceTBN(mesh, fh, &_out->t, &_out->b, &_out->n, !weighted);
811 weight *= mesh->calc_sector_angle(_h);
815 weight *= mesh->calc_sector_area(_h);
828 return ((b | (n % t)) < 0.0f) ? -1.0f : 1.0f;
831 void TangentSpace::TangentBasis::computeParity()
833 parity = ((b | (n % t)) < 0.0f) ? -1.0f : 1.0f;
836 void TangentSpace::TangentBasis::orthonormalize(
int method)
841 if ( fabsf( fabsf(t | b) - 1.0f ) < cmp_eps )
843 std::cerr <<
"warning: degenerated uv mapping" << std::endl;
847 if ( fabsf( fabsf(n | t) - 1.0f ) < cmp_eps )
849 std::cerr <<
"warning: degenerated uv mapping" << std::endl;
855 const float prevParity = parity;
861 if (method == TangentSpace::DECOMP_GRAM_SCHMIDT)
868 b = (n % t) * parity;
870 else if (method == TangentSpace::DECOMP_HALF_ANGLE)
897 float handedness = (tH | t) > 0.0f ? 1.0f : -1.0f;
899 t = h + tH * handedness;
904 b = (n % t) * parity;
906 else if (method == TangentSpace::DECOMP_POLAR)
920 for (
int r = 0; r < 3; ++r)
927 Eigen::JacobiSVD<Eigen::Matrix3f, Eigen::FullPivHouseholderQRPreconditioner> svd(M, Eigen::ComputeFullU | Eigen::ComputeFullV);
929 Eigen::Matrix3f R = svd.matrixU() * svd.matrixV().transpose();
931 for (
int r = 0; r < 3; ++r)
952 orthonormalize(TangentSpace::DECOMP_HALF_ANGLE);
953 #endif // ENABLE_EIGEN3
958 if (parity != prevParity)
961 std::cerr <<
"warning: parity flip after orthogonalization of tbn matrix" << std::endl;
965 void TangentSpace::TangentBasis::setZero()
971 void TangentSpace::TangentBasis::normalize()
978 void TangentSpace::TangentBasis::add(
const TangentBasis& _r )
986 #if QT_VERSION < 0x050000
TriMesh * triMesh(BaseObjectData *_object)
Get a triangle mesh from an object.
Type for a MeshObject containing a triangle mesh.
void pluginsInitialized()
Set the scripting slot descriptions.
VectorT< float, 3 > Vec3f
Kernel::FaceHalfedgeIter FaceHalfedgeIter
Circulator.
#define DATA_TRIANGLE_MESH
TriMeshObject * triMeshObject(BaseObjectData *_object)
Cast an BaseObject to a TriMeshObject if possible.
VectorT< float, 4 > Vec4f
const UpdateType UPDATE_ALL(UpdateTypeSet(1))
Identifier for all updates.
Kernel::VertexIHalfedgeIter VertexIHalfedgeIter
Circulator.
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.