14#ifdef LAGRANGE_WITH_MIKKTSPACE
16 #include <lagrange/SurfaceMesh.h>
17 #include <lagrange/internal/find_attribute_utils.h>
18 #include <lagrange/utils/assert.h>
19 #include <lagrange/utils/safe_cast.h>
20 #include <lagrange/views.h>
22 #include <mikktspace.h>
23 #include <Eigen/Geometry>
45template <
typename Scalar,
typename Index,
int NVPF = 3,
int DIM = 3,
int UV_DIM = 2>
46lagrange::TangentBitangentResult compute_tangent_bitangent_mikktspace(
47 lagrange::SurfaceMesh<Scalar, Index>& mesh,
48 lagrange::TangentBitangentOptions options = {})
52 static_assert(NVPF == 3);
53 static_assert(DIM == 3);
54 static_assert(UV_DIM == 2);
56 lagrange::TangentBitangentResult result;
60 options.uv_attribute_name,
67 options.normal_attribute_name,
73 const size_t num_channels = (options.pad_with_sign ? 4 : 3);
77 options.tangent_attribute_name,
81 internal::ResetToDefault::No);
85 options.bitangent_attribute_name,
89 internal::ResetToDefault::No);
95 bool orthogonalize_bitangent;
97 span<const Scalar> position_values;
98 span<const Index> position_indices;
100 span<const Scalar> normal_values;
101 span<const Index> normal_indices;
103 span<const Scalar> uv_values;
104 span<const Index> uv_indices;
106 span<Scalar> tangents;
107 span<Scalar> bitangents;
112 data.num_channels =
static_cast<int>(num_channels);
113 data.orthogonalize_bitangent = options.orthogonalize_bitangent;
114 data.position_values = mesh.get_vertex_to_position().get_all();
115 data.position_indices = mesh.get_corner_to_vertex().get_all();
116 data.normal_values = mesh.template get_indexed_attribute<Scalar>(normal_id).values().get_all();
117 data.normal_indices =
118 mesh.template get_indexed_attribute<Scalar>(normal_id).indices().get_all();
119 data.uv_values = mesh.template get_indexed_attribute<Scalar>(uv_id).values().get_all();
120 data.uv_indices = mesh.template get_indexed_attribute<Scalar>(uv_id).indices().get_all();
121 data.tangents = mesh.template ref_attribute<Scalar>(result.tangent_id).ref_all();
122 data.bitangents = mesh.template ref_attribute<Scalar>(result.bitangent_id).ref_all();
124 SMikkTSpaceInterface mk_interface;
125 mk_interface.m_getNumFaces = [](
const SMikkTSpaceContext* pContext) {
126 auto _data =
reinterpret_cast<const LocalData*
>(pContext->m_pUserData);
127 return _data->num_facets;
129 mk_interface.m_getNumVerticesOfFace = [](
const SMikkTSpaceContext* pContext,
const int iFace) {
134 mk_interface.m_getPosition =
135 [](
const SMikkTSpaceContext* pContext,
float fvPosOut[],
const int iFace,
const int iVert) {
136 auto _data =
reinterpret_cast<const LocalData*
>(pContext->m_pUserData);
137 const Index v = _data->position_indices[iFace * NVPF + iVert];
138 auto pos = _data->position_values.subspan(v * DIM, DIM);
139 fvPosOut[0] =
static_cast<float>(pos[0]);
140 fvPosOut[1] =
static_cast<float>(pos[1]);
141 fvPosOut[2] =
static_cast<float>(pos[2]);
143 mk_interface.m_getNormal = [](
const SMikkTSpaceContext* pContext,
147 auto _data =
reinterpret_cast<const LocalData*
>(pContext->m_pUserData);
148 const Index v = _data->normal_indices[iFace * NVPF + iVert];
149 auto nrm = _data->normal_values.subspan(v * DIM, DIM);
150 fvNormOut[0] =
static_cast<float>(nrm[0]);
151 fvNormOut[1] =
static_cast<float>(nrm[1]);
152 fvNormOut[2] =
static_cast<float>(nrm[2]);
154 mk_interface.m_getTexCoord = [](
const SMikkTSpaceContext* pContext,
158 auto _data =
reinterpret_cast<const LocalData*
>(pContext->m_pUserData);
159 const Index v = _data->uv_indices[iFace * NVPF + iVert];
160 auto uv = _data->uv_values.subspan(v * UV_DIM, UV_DIM);
161 fvTexcOut[0] =
static_cast<float>(uv[0]);
162 fvTexcOut[1] =
static_cast<float>(uv[1]);
164 mk_interface.m_setTSpaceBasic = [](
const SMikkTSpaceContext* pContext,
165 const float fvTangent[],
169 auto _data =
reinterpret_cast<LocalData*
>(pContext->m_pUserData);
170 auto tangent = _data->tangents.subspan(
171 (iFace * NVPF + iVert) * _data->num_channels,
172 _data->num_channels);
173 tangent[0] =
static_cast<Scalar>(fvTangent[0]);
174 tangent[1] =
static_cast<Scalar>(fvTangent[1]);
175 tangent[2] =
static_cast<Scalar>(fvTangent[2]);
176 if (tangent.size() == 4) {
177 tangent[3] =
static_cast<Scalar>(fSign);
180 mk_interface.m_setTSpace = [](
const SMikkTSpaceContext* pContext,
181 const float fvTangent[],
182 const float fvBiTangent[],
185 const tbool bIsOrientationPreserving,
190 auto _data =
reinterpret_cast<LocalData*
>(pContext->m_pUserData);
191 const float fSign = bIsOrientationPreserving ? 1.0f : (-1.0f);
193 auto tangent = _data->tangents.subspan(
194 (iFace * NVPF + iVert) * _data->num_channels,
195 _data->num_channels);
196 tangent[0] =
static_cast<Scalar>(fvTangent[0]);
197 tangent[1] =
static_cast<Scalar>(fvTangent[1]);
198 tangent[2] =
static_cast<Scalar>(fvTangent[2]);
199 if (tangent.size() == 4) {
200 tangent[3] =
static_cast<Scalar>(fSign);
203 auto bitangent = _data->bitangents.subspan(
204 (iFace * NVPF + iVert) * _data->num_channels,
205 _data->num_channels);
206 if (_data->orthogonalize_bitangent) {
207 const Index v = _data->normal_indices[iFace * NVPF + iVert];
208 auto nrm = _data->normal_values.subspan(v * DIM, DIM);
209 Eigen::Vector3f n(nrm[0], nrm[1], nrm[2]);
210 Eigen::Vector3f t(fvTangent[0], fvTangent[1], fvTangent[2]);
211 Eigen::Vector3f b = fSign * n.cross(t);
212 bitangent[0] =
static_cast<Scalar>(b[0]);
213 bitangent[1] =
static_cast<Scalar>(b[1]);
214 bitangent[2] =
static_cast<Scalar>(b[2]);
216 bitangent[0] =
static_cast<Scalar>(fvBiTangent[0]);
217 bitangent[1] =
static_cast<Scalar>(fvBiTangent[1]);
218 bitangent[2] =
static_cast<Scalar>(fvBiTangent[2]);
220 if (bitangent.size() == 4) {
221 bitangent[3] =
static_cast<Scalar>(fSign);
225 SMikkTSpaceContext context;
226 context.m_pInterface = &mk_interface;
227 context.m_pUserData =
reinterpret_cast<void*
>(&data);
230 genTangSpaceDefault(&context);
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:40
constexpr AttributeId invalid_attribute_id()
Invalid attribute id.
Definition AttributeFwd.h:76
@ Tangent
Mesh attribute can have dim or dim + 1 channels.
Definition AttributeFwd.h:59
@ Normal
Mesh attribute can have dim or dim + 1 channels.
Definition AttributeFwd.h:58
@ UV
Mesh attribute must have exactly 2 channels.
Definition AttributeFwd.h:62
@ Bitangent
Mesh attribute can have dim or dim + 1 channels.
Definition AttributeFwd.h:60
@ Scalar
Mesh attribute must have exactly 1 channel.
Definition AttributeFwd.h:56
@ Indexed
Indexed mesh attributes.
Definition AttributeFwd.h:45
@ Corner
Per-corner mesh attributes.
Definition AttributeFwd.h:37
#define la_runtime_assert(...)
Runtime assertion check.
Definition assert.h:177
constexpr auto safe_cast(SourceType value) -> std::enable_if_t<!std::is_same< SourceType, TargetType >::value, TargetType >
Perform safe cast from SourceType to TargetType, where "safe" means:
Definition safe_cast.h:51
AttributeId find_matching_attribute(const SurfaceMesh< Scalar, Index > &mesh, std::string_view name, BitField< AttributeElement > expected_element, AttributeUsage expected_usage, size_t expected_channels)
Find an attribute with a given name, ensuring the usage and element type match an expected target.
Definition find_attribute_utils.cpp:90
AttributeId find_or_create_attribute(SurfaceMesh< Scalar, Index > &mesh, std::string_view name, AttributeElement expected_element, AttributeUsage expected_usage, size_t expected_channels, ResetToDefault reset_tag)
Either retrieve or create an attribute with a prescribed name, element type and usage.
Definition find_attribute_utils.cpp:178
Main namespace for Lagrange.