Lagrange
Loading...
Searching...
No Matches
compute_tangent_bitangent_mikktspace.h
1/*
2 * Copyright 2022 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12#pragma once
13
14#ifdef LAGRANGE_WITH_MIKKTSPACE
15
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>
21
22 #include <mikktspace.h>
23 #include <Eigen/Geometry>
24
25namespace lagrange {
26
27namespace {
28
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 = {})
49{
50 la_runtime_assert(mesh.get_dimension() == DIM, "Mesh must be 3D");
51 la_runtime_assert(mesh.is_triangle_mesh(), "Input must be a triangle mesh");
52 static_assert(NVPF == 3);
53 static_assert(DIM == 3);
54 static_assert(UV_DIM == 2);
55
56 lagrange::TangentBitangentResult result;
57
59 mesh,
60 options.uv_attribute_name,
63 UV_DIM);
64 la_runtime_assert(uv_id != invalid_attribute_id(), "Mesh must have indexed UVs");
66 mesh,
67 options.normal_attribute_name,
70 DIM);
71 la_runtime_assert(normal_id != invalid_attribute_id(), "Mesh must have indexed normals");
72
73 const size_t num_channels = (options.pad_with_sign ? 4 : 3);
74
76 mesh,
77 options.tangent_attribute_name,
80 num_channels,
81 internal::ResetToDefault::No);
82
84 mesh,
85 options.bitangent_attribute_name,
88 num_channels,
89 internal::ResetToDefault::No);
90
91 struct LocalData
92 {
93 int num_facets;
94 int num_channels;
95 bool orthogonalize_bitangent;
96
97 span<const Scalar> position_values;
98 span<const Index> position_indices;
99
100 span<const Scalar> normal_values;
101 span<const Index> normal_indices;
102
103 span<const Scalar> uv_values;
104 span<const Index> uv_indices;
105
106 span<Scalar> tangents;
107 span<Scalar> bitangents;
108 };
109
110 LocalData data;
111 data.num_facets = lagrange::safe_cast<int>(mesh.get_num_facets());
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();
123
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;
128 };
129 mk_interface.m_getNumVerticesOfFace = [](const SMikkTSpaceContext* pContext, const int iFace) {
130 (void)pContext;
131 (void)iFace;
132 return NVPF;
133 };
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]);
142 };
143 mk_interface.m_getNormal = [](const SMikkTSpaceContext* pContext,
144 float fvNormOut[],
145 const int iFace,
146 const int iVert) {
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]);
153 };
154 mk_interface.m_getTexCoord = [](const SMikkTSpaceContext* pContext,
155 float fvTexcOut[],
156 const int iFace,
157 const int iVert) {
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]);
163 };
164 mk_interface.m_setTSpaceBasic = [](const SMikkTSpaceContext* pContext,
165 const float fvTangent[],
166 const float fSign,
167 const int iFace,
168 const int iVert) {
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);
178 }
179 };
180 mk_interface.m_setTSpace = [](const SMikkTSpaceContext* pContext,
181 const float fvTangent[],
182 const float fvBiTangent[],
183 const float fMagS,
184 const float fMagT,
185 const tbool bIsOrientationPreserving,
186 const int iFace,
187 const int iVert) {
188 (void)fMagS;
189 (void)fMagT;
190 auto _data = reinterpret_cast<LocalData*>(pContext->m_pUserData);
191 const float fSign = bIsOrientationPreserving ? 1.0f : (-1.0f);
192
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);
201 }
202
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]);
215 } else {
216 bitangent[0] = static_cast<Scalar>(fvBiTangent[0]);
217 bitangent[1] = static_cast<Scalar>(fvBiTangent[1]);
218 bitangent[2] = static_cast<Scalar>(fvBiTangent[2]);
219 }
220 if (bitangent.size() == 4) {
221 bitangent[3] = static_cast<Scalar>(fSign);
222 }
223 };
224
225 SMikkTSpaceContext context;
226 context.m_pInterface = &mk_interface;
227 context.m_pUserData = reinterpret_cast<void*>(&data);
228
229 lagrange::logger().debug("run mikktspace");
230 genTangSpaceDefault(&context);
231 lagrange::logger().debug("done");
232
233 return result;
234}
235
236} // namespace
237
238} // namespace lagrange
239
240#endif
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.