14#include <lagrange/Mesh.h>
15#include <lagrange/MeshTrait.h>
16#include <lagrange/chain_corners_around_edges.h>
17#include <lagrange/chain_corners_around_vertices.h>
18#include <lagrange/common.h>
19#include <lagrange/corner_to_edge_mapping.h>
20#include <lagrange/legacy/inline.h>
21#include <lagrange/utils/DisjointSets.h>
22#include <lagrange/utils/geometry3d.h>
25#include <lagrange/utils/warnoff.h>
26#include <tbb/parallel_for.h>
27#include <lagrange/utils/warnon.h>
36namespace tangent_bitangent_detail {
49template <
typename Scalar>
50Eigen::Matrix<Scalar, 2, 3> triangle_tangent_bitangent(
51 const Eigen::Matrix<Scalar, 3, 3>& vertices,
52 const Eigen::Matrix<Scalar, 3, 2>& uvs,
55 const auto edge0 = (vertices.row(1) - vertices.row(0)).eval();
56 const auto edge1 = (vertices.row(2) - vertices.row(0)).eval();
58 const auto edge_uv0 = (uvs.row(1) - uvs.row(0)).eval();
59 const auto edge_uv1 = (uvs.row(2) - uvs.row(0)).eval();
61 const Scalar uv_signed_area = (edge_uv0.x() * edge_uv1.y() - edge_uv0.y() * edge_uv1.x());
62 const Scalar r = (uv_signed_area == Scalar(0) ? Scalar(1) : Scalar(1) / uv_signed_area);
64 sign = (uv_signed_area > 0);
66 Eigen::Matrix<Scalar, 2, 3> T_BT;
67 T_BT.row(0) = ((edge0 * edge_uv1.y() - edge1 * edge_uv0.y()) * r).stableNormalized();
68 T_BT.row(1) = ((edge1 * edge_uv0.x() - edge0 * edge_uv1.x()) * r).stableNormalized();
81template <
typename Scalar>
82bool triangle_uv_orientation(
const Eigen::Matrix<Scalar, 3, 2>& uvs)
84 const auto edge_uv0 = (uvs.row(1) - uvs.row(0)).eval();
85 const auto edge_uv1 = (uvs.row(2) - uvs.row(0)).eval();
87 const Scalar uv_signed_area = (edge_uv0.x() * edge_uv1.y() - edge_uv0.y() * edge_uv1.x());
89 return uv_signed_area > 0;
104template <
typename Scalar>
105Eigen::Matrix<Scalar, 2, 3> quad_tangent_bitangent(
106 const Eigen::Matrix<Scalar, 4, 3>& vertices,
107 const Eigen::Matrix<Scalar, 4, 2>& uvs,
111 Eigen::Vector3i indices = Eigen::Vector3i(0, 1, 2);
112 if ((uvs.row(0) - uvs.row(1)).isZero()) {
113 indices = Eigen::Vector3i(1, 2, 3);
114 }
else if ((uvs.row(1) - uvs.row(2)).isZero()) {
115 indices = Eigen::Vector3i(2, 3, 0);
116 }
else if ((uvs.row(2) - uvs.row(3)).isZero()) {
117 indices = Eigen::Vector3i(3, 0, 1);
120 Eigen::Matrix<Scalar, 3, 3> triangle_vertices;
121 triangle_vertices.row(0) = vertices.row(indices(0));
122 triangle_vertices.row(1) = vertices.row(indices(1));
123 triangle_vertices.row(2) = vertices.row(indices(2));
125 Eigen::Matrix<Scalar, 3, 2> triangle_uvs;
126 triangle_uvs.row(0) = uvs.row(indices(0));
127 triangle_uvs.row(1) = uvs.row(indices(1));
128 triangle_uvs.row(2) = uvs.row(indices(2));
131 return triangle_tangent_bitangent(triangle_vertices, triangle_uvs,
sign);
148template <
typename MeshType,
typename DerivedT,
typename DerivedB>
149void accumulate_tangent_bitangent(
151 const Eigen::MatrixBase<DerivedT>& tangents,
152 const Eigen::MatrixBase<DerivedB>& bitangents)
154 static_assert(MeshTrait<MeshType>::is_mesh(),
"Input type is not Mesh");
155 using Scalar =
typename MeshType::Scalar;
156 using Index =
typename MeshType::Index;
157 using FacetArray =
typename MeshType::FacetArray;
158 using AttributeArray =
typename MeshType::AttributeArray;
159 using IndexArray = Eigen::Matrix<Index, Eigen::Dynamic, 1>;
160 using RowVector3r = Eigen::Matrix<Scalar, 1, 3>;
163 mesh.get_vertex_per_facet() == 3,
164 "Only triangle meshes are supported for this.");
165 la_runtime_assert(safe_cast<Index>(tangents.rows()) == mesh.get_num_facets() * 3);
166 la_runtime_assert(safe_cast<Index>(bitangents.rows()) == mesh.get_num_facets() * 3);
171 logger().trace(
"Corner to edge mapping");
174 logger().trace(
"Chain corners around edges");
176 IndexArray next_corner_around_edge;
178 logger().trace(
"Chain corners around vertices");
180 IndexArray next_corner_around_vertex;
182 mesh.get_num_vertices(),
185 next_corner_around_vertex);
187 const auto nvpf = mesh.get_vertex_per_facet();
188 const auto& vertices = mesh.get_vertices();
189 const auto& facets = mesh.get_facets();
190 const auto& uv_values = std::get<0>(mesh.get_indexed_attribute(
"uv"));
191 const auto& uv_indices = std::get<1>(mesh.get_indexed_attribute(
"uv"));
192 const auto& normal_values = std::get<0>(mesh.get_indexed_attribute(
"normal"));
193 const auto& normal_indices = std::get<1>(mesh.get_indexed_attribute(
"normal"));
196 logger().trace(
"Is orient preserving?");
197 std::vector<unsigned char> is_orient_preserving(mesh.get_num_facets());
198 tbb::parallel_for(Index(0), mesh.get_num_facets(), [&](Index f) {
199 Eigen::Matrix<Scalar, 3, 2> uvs;
200 uvs.row(0) = uv_values.row(uv_indices(f, 0));
201 uvs.row(1) = uv_values.row(uv_indices(f, 1));
202 uvs.row(2) = uv_values.row(uv_indices(f, 2));
203 is_orient_preserving[f] = triangle_uv_orientation(uvs);
208 auto is_edge_smooth = [&](Index eij, Index fi, Index fj) {
209 std::array<Index, 2> v, n, uv;
210 std::fill(v.begin(), v.end(), invalid<Index>());
211 for (Index c = e2c[eij]; c != invalid<Index>(); c = next_corner_around_edge[c]) {
214 if (f == fi || f == fj) {
215 Index v0 = facets(f, lv);
216 Index v1 = facets(f, (lv + 1) % 3);
217 Index n0 = normal_indices(f, lv);
218 Index n1 = normal_indices(f, (lv + 1) % 3);
219 Index uv0 = uv_indices(f, lv);
220 Index uv1 = uv_indices(f, (lv + 1) % 3);
221 if (v[0] == invalid<Index>()) {
236 return (n0 == n[0] && n1 == n[1] && uv0 == uv[0] && uv1 == uv[1]);
244 auto is_face_degenerate = [&](Index f) {
245 for (Index lv = 0; lv < nvpf; ++lv) {
246 if (facets(f, lv) == facets(f, (lv + 1) % nvpf)) {
255 logger().trace(
"Loop to unify corner indices");
256 DisjointSets<Index> unified_indices((Index)tangents.rows());
257 tbb::parallel_for(Index(0), Index(mesh.get_num_vertices()), [&](Index v) {
258 for (Index ci = v2c[v]; ci != invalid<Index>(); ci = next_corner_around_vertex[ci]) {
262 Index vi = facets(fi, lvi);
263 if (is_face_degenerate(fi)) continue;
265 auto si = is_orient_preserving[fi];
266 for (Index cj = e2c[eij]; cj != invalid<Index>(); cj = next_corner_around_edge[cj]) {
269 auto sj = is_orient_preserving[fj];
270 if (fi == fj) continue;
271 if (si != sj) continue;
272 Index vj = facets(fj, lvj);
275 vj = facets(fj, lvj);
279 if (is_edge_smooth(eij, fi, fj)) {
280 unified_indices.merge(ci, fj * 3 + lvj);
287 logger().trace(
"Compute new indices");
288 const Index num_corners = (Index)tangents.rows();
289 std::vector<Index> repr(num_corners, invalid<Index>());
290 Index num_indices = 0;
291 for (Index n = 0; n < num_corners; ++n) {
292 Index r = unified_indices.find(n);
293 if (repr[r] == invalid<Index>()) {
294 repr[r] = num_indices++;
298 logger().trace(
"Compute offsets");
299 std::vector<Index> indices(num_corners);
300 std::vector<Index> offsets(num_indices + 1, 0);
301 for (Index c = 0; c < num_corners; ++c) {
302 offsets[repr[c] + 1]++;
304 for (Index r = 1; r <= num_indices; ++r) {
305 offsets[r] += offsets[r - 1];
309 std::vector<Index> counter = offsets;
310 for (Index c = 0; c < num_corners; ++c) {
311 indices[counter[repr[c]]++] = c;
315 logger().trace(
"Project and average tangent/bitangent");
316 FacetArray tangent_indices(mesh.get_num_facets(), 3);
317 AttributeArray tangent_values(num_indices, tangents.cols());
318 AttributeArray bitangent_values(num_indices, bitangents.cols());
319 tangent_values.setZero();
320 bitangent_values.setZero();
321 tbb::parallel_for(Index(0), Index(offsets.size() - 1), [&](Index r) {
322 for (Index i = offsets[r]; i < offsets[r + 1]; ++i) {
323 Index c = indices[i];
326 assert(repr[c] == r);
329 RowVector3r nrm = normal_values.row(normal_indices(f, lv));
330 RowVector3r t = tangents.row(c).template head<3>();
331 RowVector3r bt = bitangents.row(c).template head<3>();
332 t = project_on_plane(t, nrm).stableNormalized();
333 bt = project_on_plane(bt, nrm).stableNormalized();
336 RowVector3r v0 = vertices.row(facets(f, lv));
337 RowVector3r v1 = vertices.row(facets(f, (lv + 1) % 3));
338 RowVector3r v2 = vertices.row(facets(f, (lv + 2) % 3));
339 RowVector3r e01 = v1 - v0;
340 RowVector3r e02 = v2 - v0;
341 Scalar w = projected_angle_between(e01, e02, nrm);
343 tangent_values.row(r).template head<3>() += t * w;
344 bitangent_values.row(r).template head<3>() += bt * w;
345 if (tangents.cols() == 4) {
346 if (tangent_values(r, 3) == 0) {
347 tangent_values(r, 3) = tangents(c, 3);
348 bitangent_values(r, 3) = bitangents(c, 3);
350 assert(tangent_values(r, 3) == tangents(c, 3));
351 assert(bitangent_values(r, 3) == bitangents(c, 3));
355 tangent_indices(f, lv) = r;
358 logger().trace(
"Normalize vectors");
359 tbb::parallel_for(Index(0), Index(tangent_values.rows()), [&](Index c) {
360 tangent_values.row(c).template head<3>().stableNormalize();
361 bitangent_values.row(c).template head<3>().stableNormalize();
364 logger().trace(
"Write attributes");
365 FacetArray bitangent_indices = tangent_indices;
366 mesh.add_indexed_attribute(
"tangent");
367 mesh.add_indexed_attribute(
"bitangent");
368 mesh.import_indexed_attribute(
"tangent", tangent_values, tangent_indices);
369 mesh.import_indexed_attribute(
"bitangent", bitangent_values, bitangent_indices);
386template <
typename MeshType,
typename DerivedT,
typename DerivedB>
387void corner_tangent_bitangent_raw(
389 Eigen::PlainObjectBase<DerivedT>& T,
390 Eigen::PlainObjectBase<DerivedB>& BT,
393 static_assert(MeshTrait<MeshType>::is_mesh(),
"Input type is not Mesh");
398 using Scalar =
typename MeshType::Scalar;
399 using Index =
typename MeshType::Index;
401 const auto& F = mesh.get_facets();
402 const auto& V = mesh.get_vertices();
403 const auto& UV_val = mesh.get_uv();
404 const auto& UV_indices = mesh.get_uv_indices();
406 const auto n_faces = mesh.get_num_facets();
407 const auto per_facet = mesh.get_vertex_per_facet();
409 T.resize(n_faces * per_facet, pad_with_sign ? 4 : 3);
410 BT.resize(n_faces * per_facet, pad_with_sign ? 4 : 3);
413 if (per_facet == 3) {
414 tbb::parallel_for(Index(0), Index(F.rows()), [&](Index i) {
415 const auto& indices = F.row(i);
416 Eigen::Matrix<Scalar, 3, 3> vertices;
417 vertices.row(0) = V.row(indices(0));
418 vertices.row(1) = V.row(indices(1));
419 vertices.row(2) = V.row(indices(2));
421 Eigen::Matrix<Scalar, 3, 2> uvs;
422 uvs.row(0) = UV_val.row(UV_indices(i, 0));
423 uvs.row(1) = UV_val.row(UV_indices(i, 1));
424 uvs.row(2) = UV_val.row(UV_indices(i, 2));
427 auto T_BT = triangle_tangent_bitangent(vertices, uvs, sign);
429 for (Index k = 0; k < per_facet; k++) {
430 T.row(i * per_facet + k).template head<3>() = T_BT.row(0);
431 BT.row(i * per_facet + k).template head<3>() = T_BT.row(1);
433 T.row(i * per_facet + k).w() = Scalar(sign ? 1 : -1);
434 BT.row(i * per_facet + k).w() = Scalar(sign ? 1 : -1);
440 else if (per_facet == 4) {
441 tbb::parallel_for(Index(0), Index(F.rows()), [&](Index i) {
442 const auto& indices = F.row(i);
445 Eigen::Matrix<Scalar, 2, 3> T_BT;
448 if (indices(3) == invalid<typename MeshType::Index>()) {
449 Eigen::Matrix<Scalar, 3, 3> vertices;
450 vertices.row(0) = V.row(indices(0));
451 vertices.row(1) = V.row(indices(1));
452 vertices.row(2) = V.row(indices(2));
454 Eigen::Matrix<Scalar, 3, 2> uvs;
455 uvs.row(0) = UV_val.row(UV_indices(i, 0));
456 uvs.row(1) = UV_val.row(UV_indices(i, 1));
457 uvs.row(2) = UV_val.row(UV_indices(i, 2));
459 T_BT = triangle_tangent_bitangent(vertices, uvs, sign);
463 Eigen::Matrix<Scalar, 4, 3> vertices;
464 vertices.row(0) = V.row(indices(0));
465 vertices.row(1) = V.row(indices(1));
466 vertices.row(2) = V.row(indices(2));
467 vertices.row(3) = V.row(indices(3));
469 Eigen::Matrix<Scalar, 4, 2> uvs;
470 uvs.row(0) = UV_val.row(UV_indices(i, 0));
471 uvs.row(1) = UV_val.row(UV_indices(i, 1));
472 uvs.row(2) = UV_val.row(UV_indices(i, 2));
473 uvs.row(3) = UV_val.row(UV_indices(i, 3));
475 T_BT = quad_tangent_bitangent(vertices, uvs, sign);
478 for (Index k = 0; k < per_facet; k++) {
479 if (indices(k) == invalid<typename MeshType::Index>())
break;
480 T.row(i * per_facet + k).
template head<3>() = T_BT.row(0);
481 BT.row(i * per_facet + k).template head<3>() = T_BT.row(1);
483 T.row(i * per_facet + k).w() = Scalar(
sign ? 1 : -1);
484 BT.row(i * per_facet + k).w() = Scalar(
sign ? 1 : -1);
506template <
typename MeshType>
507void compute_corner_tangent_bitangent(
MeshType& mesh,
bool pad_with_sign =
false)
509 static_assert(MeshTrait<MeshType>::is_mesh(),
"Input type is not Mesh");
514 typename MeshType::AttributeArray T;
515 typename MeshType::AttributeArray BT;
516 tangent_bitangent_detail::corner_tangent_bitangent_raw(mesh, T, BT, pad_with_sign);
518 mesh.add_corner_attribute(
"tangent");
519 mesh.add_corner_attribute(
"bitangent");
520 mesh.import_corner_attribute(
"tangent", T);
521 mesh.import_corner_attribute(
"bitangent", BT);
535template <
typename MeshType>
536void compute_indexed_tangent_bitangent(
MeshType& mesh,
bool pad_with_sign =
false)
538 static_assert(MeshTrait<MeshType>::is_mesh(),
"Input type is not Mesh");
542 la_runtime_assert(mesh.get_vertex_per_facet() == 3,
"Input must be a triangle mesh");
543 la_runtime_assert(mesh.has_indexed_attribute(
"normal"),
"Mesh must have indexed normals");
545 typename MeshType::AttributeArray T;
546 typename MeshType::AttributeArray BT;
547 logger().debug(
"Compute corner tangent info");
548 tangent_bitangent_detail::corner_tangent_bitangent_raw(mesh, T, BT, pad_with_sign);
549 logger().debug(
"Accumulate tangent info");
550 tangent_bitangent_detail::accumulate_tangent_bitangent(mesh, T, BT);
LA_CORE_API spdlog::logger & logger()
Retrieves the current logger.
Definition: Logger.cpp:40
#define la_runtime_assert(...)
Runtime assertion check.
Definition: assert.h:169
constexpr void swap(function_ref< R(Args...)> &lhs, function_ref< R(Args...)> &rhs) noexcept
Swaps the referred callables of lhs and rhs.
Definition: function_ref.h:114
Main namespace for Lagrange.
Definition: AABBIGL.h:30
int sign(T val)
Get the sign of the value Returns either -1, 0, or 1.
Definition: utils.h:43
Eigen::Index corner_to_edge_mapping(const Eigen::MatrixBase< DerivedF > &F, Eigen::PlainObjectBase< DerivedC > &C2E)
Computes a mapping from mesh corners (k*f+i) to unique edge ids.
Definition: corner_to_edge_mapping.impl.h:36
void chain_corners_around_edges(const Eigen::MatrixBase< DerivedF > &facets, const Eigen::MatrixBase< DerivedC > &corner_to_edge, Eigen::PlainObjectBase< DerivedE > &edge_to_corner, Eigen::PlainObjectBase< DerivedN > &next_corner_around_edge)
Chains facet corners around edges of a mesh.
Definition: chain_corners_around_edges.h:39
void chain_corners_around_vertices(typename DerivedF::Scalar num_vertices, const Eigen::MatrixBase< DerivedF > &facets, Eigen::PlainObjectBase< DerivedE > &vertex_to_corner, Eigen::PlainObjectBase< DerivedN > &next_corner_around_vertex)
Chains facet corners around vertices of a mesh.
Definition: chain_corners_around_vertices.h:41