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
24namespace lagrange {
25
26namespace {
27
44template <typename Scalar, typename Index, int NVPF = 3, int DIM = 3, int UV_DIM = 2>
45lagrange::TangentBitangentResult compute_tangent_bitangent_mikktspace(
46 lagrange::SurfaceMesh<Scalar, Index>& mesh,
47 lagrange::TangentBitangentOptions options = {})
48{
49 la_runtime_assert(mesh.get_dimension() == DIM, "Mesh must be 3D");
50 la_runtime_assert(mesh.is_triangle_mesh(), "Input must be a triangle mesh");
51 static_assert(NVPF == 3);
52 static_assert(DIM == 3);
53 static_assert(UV_DIM == 2);
54
55 lagrange::TangentBitangentResult result;
56
58 mesh,
59 options.uv_attribute_name,
62 UV_DIM);
63 la_runtime_assert(uv_id != invalid_attribute_id(), "Mesh must have indexed UVs");
65 mesh,
66 options.normal_attribute_name,
69 DIM);
70 la_runtime_assert(normal_id != invalid_attribute_id(), "Mesh must have indexed normals");
71
72 const size_t num_channels = (options.pad_with_sign ? 4 : 3);
73
75 mesh,
76 options.tangent_attribute_name,
79 num_channels,
80 internal::ResetToDefault::No);
81
83 mesh,
84 options.bitangent_attribute_name,
87 num_channels,
88 internal::ResetToDefault::No);
89
90 struct LocalData
91 {
92 int num_facets;
93 int num_channels;
94 bool orthogonalize_bitangent;
95
96 span<const Scalar> position_values;
97 span<const Index> position_indices;
98
99 span<const Scalar> normal_values;
100 span<const Index> normal_indices;
101
102 span<const Scalar> uv_values;
103 span<const Index> uv_indices;
104
105 span<Scalar> tangents;
106 span<Scalar> bitangents;
107 };
108
109 LocalData data;
110 data.num_facets = lagrange::safe_cast<int>(mesh.get_num_facets());
111 data.num_channels = static_cast<int>(num_channels);
112 data.orthogonalize_bitangent = options.orthogonalize_bitangent;
113 data.position_values = mesh.get_vertex_to_position().get_all();
114 data.position_indices = mesh.get_corner_to_vertex().get_all();
115 data.normal_values = mesh.template get_indexed_attribute<Scalar>(normal_id).values().get_all();
116 data.normal_indices =
117 mesh.template get_indexed_attribute<Scalar>(normal_id).indices().get_all();
118 data.uv_values = mesh.template get_indexed_attribute<Scalar>(uv_id).values().get_all();
119 data.uv_indices = mesh.template get_indexed_attribute<Scalar>(uv_id).indices().get_all();
120 data.tangents = mesh.template ref_attribute<Scalar>(result.tangent_id).ref_all();
121 data.bitangents = mesh.template ref_attribute<Scalar>(result.bitangent_id).ref_all();
122
123 SMikkTSpaceInterface mk_interface;
124 mk_interface.m_getNumFaces = [](const SMikkTSpaceContext* pContext) {
125 auto _data = reinterpret_cast<const LocalData*>(pContext->m_pUserData);
126 return _data->num_facets;
127 };
128 mk_interface.m_getNumVerticesOfFace = [](const SMikkTSpaceContext* pContext, const int iFace) {
129 (void)pContext;
130 (void)iFace;
131 return NVPF;
132 };
133 mk_interface.m_getPosition =
134 [](const SMikkTSpaceContext* pContext, float fvPosOut[], const int iFace, const int iVert) {
135 auto _data = reinterpret_cast<const LocalData*>(pContext->m_pUserData);
136 const Index v = _data->position_indices[iFace * NVPF + iVert];
137 auto pos = _data->position_values.subspan(v * DIM, DIM);
138 fvPosOut[0] = static_cast<float>(pos[0]);
139 fvPosOut[1] = static_cast<float>(pos[1]);
140 fvPosOut[2] = static_cast<float>(pos[2]);
141 };
142 mk_interface.m_getNormal = [](const SMikkTSpaceContext* pContext,
143 float fvNormOut[],
144 const int iFace,
145 const int iVert) {
146 auto _data = reinterpret_cast<const LocalData*>(pContext->m_pUserData);
147 const Index v = _data->normal_indices[iFace * NVPF + iVert];
148 auto nrm = _data->normal_values.subspan(v * DIM, DIM);
149 fvNormOut[0] = static_cast<float>(nrm[0]);
150 fvNormOut[1] = static_cast<float>(nrm[1]);
151 fvNormOut[2] = static_cast<float>(nrm[2]);
152 };
153 mk_interface.m_getTexCoord = [](const SMikkTSpaceContext* pContext,
154 float fvTexcOut[],
155 const int iFace,
156 const int iVert) {
157 auto _data = reinterpret_cast<const LocalData*>(pContext->m_pUserData);
158 const Index v = _data->uv_indices[iFace * NVPF + iVert];
159 auto uv = _data->uv_values.subspan(v * UV_DIM, UV_DIM);
160 fvTexcOut[0] = static_cast<float>(uv[0]);
161 fvTexcOut[1] = static_cast<float>(uv[1]);
162 };
163 mk_interface.m_setTSpaceBasic = [](const SMikkTSpaceContext* pContext,
164 const float fvTangent[],
165 const float fSign,
166 const int iFace,
167 const int iVert) {
168 auto _data = reinterpret_cast<LocalData*>(pContext->m_pUserData);
169 auto tangent = _data->tangents.subspan(
170 (iFace * NVPF + iVert) * _data->num_channels,
171 _data->num_channels);
172 tangent[0] = static_cast<Scalar>(fvTangent[0]);
173 tangent[1] = static_cast<Scalar>(fvTangent[1]);
174 tangent[2] = static_cast<Scalar>(fvTangent[2]);
175 if (tangent.size() == 4) {
176 tangent[3] = static_cast<Scalar>(fSign);
177 }
178 };
179 mk_interface.m_setTSpace = [](const SMikkTSpaceContext* pContext,
180 const float fvTangent[],
181 const float fvBiTangent[],
182 const float fMagS,
183 const float fMagT,
184 const tbool bIsOrientationPreserving,
185 const int iFace,
186 const int iVert) {
187 (void)fMagS;
188 (void)fMagT;
189 auto _data = reinterpret_cast<LocalData*>(pContext->m_pUserData);
190 const float fSign = bIsOrientationPreserving ? 1.0f : (-1.0f);
191
192 auto tangent = _data->tangents.subspan(
193 (iFace * NVPF + iVert) * _data->num_channels,
194 _data->num_channels);
195 tangent[0] = static_cast<Scalar>(fvTangent[0]);
196 tangent[1] = static_cast<Scalar>(fvTangent[1]);
197 tangent[2] = static_cast<Scalar>(fvTangent[2]);
198 if (tangent.size() == 4) {
199 tangent[3] = static_cast<Scalar>(fSign);
200 }
201
202 auto bitangent = _data->bitangents.subspan(
203 (iFace * NVPF + iVert) * _data->num_channels,
204 _data->num_channels);
205 if (_data->orthogonalize_bitangent) {
206 const Index v = _data->normal_indices[iFace * NVPF + iVert];
207 auto nrm = _data->normal_values.subspan(v * DIM, DIM);
208 Eigen::Vector3f n(nrm[0], nrm[1], nrm[2]);
209 Eigen::Vector3f t(fvTangent[0], fvTangent[1], fvTangent[2]);
210 Eigen::Vector3f b = fSign * n.cross(t);
211 bitangent[0] = static_cast<Scalar>(b[0]);
212 bitangent[1] = static_cast<Scalar>(b[1]);
213 bitangent[2] = static_cast<Scalar>(b[2]);
214 } else {
215 bitangent[0] = static_cast<Scalar>(fvBiTangent[0]);
216 bitangent[1] = static_cast<Scalar>(fvBiTangent[1]);
217 bitangent[2] = static_cast<Scalar>(fvBiTangent[2]);
218 }
219 if (bitangent.size() == 4) {
220 bitangent[3] = static_cast<Scalar>(fSign);
221 }
222 };
223
224 SMikkTSpaceContext context;
225 context.m_pInterface = &mk_interface;
226 context.m_pUserData = reinterpret_cast<void*>(&data);
227
228 lagrange::logger().debug("run mikktspace");
229 genTangSpaceDefault(&context);
230 lagrange::logger().debug("done");
231
232 return result;
233}
234
235} // namespace
236
237} // namespace lagrange
238
239#endif
lagrange::span< const ValueType > get_all() const
Returns a read-only view of the buffer spanning num elements x num channels.
Definition Attribute.cpp:512
Index get_dimension() const
Retrieves the dimension of the mesh vertices.
Definition SurfaceMesh.h:656
const Attribute< Index > & get_corner_to_vertex() const
Gets a read-only reference to the corner -> vertex id attribute.
Definition SurfaceMesh.cpp:1393
bool is_triangle_mesh() const
Whether the mesh must only contain triangular facets.
Definition SurfaceMesh.cpp:2098
Index get_num_facets() const
Retrieves the number of facets.
Definition SurfaceMesh.h:678
const Attribute< Scalar > & get_vertex_to_position() const
Gets a read-only reference to the vertex -> positions attribute.
Definition SurfaceMesh.cpp:1381
LA_CORE_API 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:174
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:50
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:97
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:185
Main namespace for Lagrange.
AttributeId tangent_id
Tangent vector attribute id.
Definition compute_tangent_bitangent.h:77
AttributeId bitangent_id
Bitangent vector attribute id.
Definition compute_tangent_bitangent.h:80