18#include <lagrange/MeshTrait.h>
19#include <lagrange/attributes/attribute_utils.h>
20#include <lagrange/common.h>
21#include <lagrange/create_mesh.h>
22#include <lagrange/normalize_meshes.h>
23#include <lagrange/utils/range.h>
24#include <lagrange/utils/safe_cast.h>
25#include <lagrange/legacy/inline.h>
27#include <lagrange/fs/filesystem.h>
28#include <tiny_obj_loader.h>
47 bool load_normals =
true;
49 bool load_materials =
true;
55template <
typename MeshType>
59 std::vector<std::unique_ptr<MeshType>> meshes;
60 std::vector<tinyobj::material_t> materials;
61 std::vector<std::string> mesh_names;
64inline int fix_index(
int index,
size_t n,
size_t global_offset)
67 if (index > 0)
return index -
static_cast<int>(global_offset) - 1;
69 if (index < 0)
return static_cast<int>(n) + index -
static_cast<int>(global_offset);
85template <
typename MeshType>
87 std::istream& input_stream,
88 const MeshLoaderParams& params = {},
89 tinyobj::MaterialReader* material_reader =
nullptr) -> MeshLoaderResult<MeshType>
92 using VertexArray =
typename MeshType::VertexArray;
93 using FacetArray =
typename MeshType::FacetArray;
95 MeshLoaderResult<MeshType> result;
97 if (!input_stream.good()) {
98 logger().error(
"Invalid input stream given");
99 result.success =
false;
107 Loader(
const MeshLoaderParams& _params, MeshLoaderResult<MeshType>& _result)
112 const MeshLoaderParams& params;
113 MeshLoaderResult<MeshType>& result;
115 std::vector<tinyobj::real_t> vertices;
116 std::vector<tinyobj::real_t> normals;
117 std::vector<tinyobj::real_t> uvs;
118 std::vector<tinyobj::index_t> indices;
119 std::vector<int> material_ids;
121 size_t vertex_offset = 0;
122 size_t normal_offset = 0;
123 size_t uv_offset = 0;
125 std::string object_name =
"";
126 int current_material_id = 0;
128 std::vector<unsigned char> face_sizes;
129 int max_face_size = 0;
130 bool is_face_size_constant =
false;
131 bool is_last_object =
false;
135 std::vector<tinyobj::index_t> new_indices;
136 std::vector<unsigned char> new_face_sizes;
137 std::vector<int> new_material_ids;
138 size_t indices_i = 0;
141 for (
auto face_index :
range(face_sizes.size())) {
142 const auto vertex_in_face_num = face_sizes[face_index];
143 const auto new_triangle_num = (vertex_in_face_num - 3) + 1;
146 for (
auto i :
range(new_triangle_num)) {
149 a.vertex_index = indices[indices_i + 0].vertex_index;
150 a.normal_index = indices[indices_i + 0].normal_index;
151 a.texcoord_index = indices[indices_i + 0].texcoord_index;
155 b.vertex_index = indices[indices_i + i + 1].vertex_index;
156 b.normal_index = indices[indices_i + i + 1].normal_index;
157 b.texcoord_index = indices[indices_i + i + 1].texcoord_index;
159 c.vertex_index = indices[indices_i + i + 2].vertex_index;
160 c.normal_index = indices[indices_i + i + 2].normal_index;
161 c.texcoord_index = indices[indices_i + i + 2].texcoord_index;
163 new_indices.push_back(a);
164 new_indices.push_back(b);
165 new_indices.push_back(c);
166 new_face_sizes.push_back(3);
168 if (face_index < material_ids.size()) {
169 new_material_ids.push_back(material_ids[face_index]);
173 indices_i += vertex_in_face_num;
177 indices = new_indices;
178 face_sizes = new_face_sizes;
179 material_ids = new_material_ids;
181 is_face_size_constant =
true;
184 } mesh_loader(params, result);
189 const auto vertex_cb = [](
void* user_data,
194 Loader& loader = *(
reinterpret_cast<Loader*
>(user_data));
195 loader.vertices.insert(loader.vertices.end(), {x, y, z});
199 const auto normal_cb =
200 [](
void* user_data, tinyobj::real_t x, tinyobj::real_t y, tinyobj::real_t z) {
201 Loader& loader = *(
reinterpret_cast<Loader*
>(user_data));
202 if (loader.params.load_normals) {
203 loader.normals.insert(loader.normals.end(), {x, y, z});
207 const auto texcoord_cb =
208 [](
void* user_data, tinyobj::real_t x, tinyobj::real_t y, tinyobj::real_t z) {
209 Loader& loader = *(
reinterpret_cast<Loader*
>(user_data));
210 if (loader.params.load_uvs) {
211 loader.uvs.insert(loader.uvs.end(), {x, y});
216 const auto usemtl_cb = [](
void* user_data,
const char* name,
int material_id) {
217 Loader& loader = *(
reinterpret_cast<Loader*
>(user_data));
218 loader.current_material_id = material_id;
222 const auto mtllib_cb =
223 [](
void* user_data,
const tinyobj::material_t* materials,
int num_materials) {
224 Loader& loader = *(
reinterpret_cast<Loader*
>(user_data));
225 loader.result.materials.insert(
226 loader.result.materials.end(),
228 materials + num_materials);
231 const auto index_cb = [](
void* user_data, tinyobj::index_t* indices,
int num_indices) {
232 Loader& loader = *(
reinterpret_cast<Loader*
>(user_data));
233 auto num_vertices = loader.vertices.size() / 3;
234 auto num_normals = loader.normals.size() / 3;
235 auto num_uvs = loader.uvs.size() / 2;
236 for (
auto i = 0; i < num_indices; i++) {
237 auto index = indices[i];
238 index.vertex_index = fix_index(index.vertex_index, num_vertices, loader.vertex_offset);
239 assert(index.vertex_index >= 0 && index.vertex_index <
static_cast<int>(num_vertices));
240 if (loader.params.load_normals && num_normals) {
242 fix_index(index.normal_index, num_normals, loader.normal_offset);
244 index.normal_index >= 0 && index.normal_index <
static_cast<int>(num_normals));
246 index.normal_index = -1;
248 if (loader.params.load_uvs && num_uvs) {
249 index.texcoord_index = fix_index(index.texcoord_index, num_uvs, loader.uv_offset);
251 index.texcoord_index >= 0 && index.texcoord_index <
static_cast<int>(num_uvs));
253 index.texcoord_index = -1;
255 loader.indices.push_back(index);
258 loader.face_sizes.push_back(
static_cast<unsigned char>(num_indices));
259 if (loader.params.load_materials) {
260 loader.material_ids.push_back(loader.current_material_id);
263 if (loader.max_face_size != 0 && num_indices != loader.max_face_size) {
264 loader.is_face_size_constant =
false;
266 loader.max_face_size = std::max(num_indices, loader.max_face_size);
269 const auto object_cb = [](
void* user_data,
const char* name) {
270 Loader& loader = *(
reinterpret_cast<Loader*
>(user_data));
273 size_t max_face_size;
275 const auto UV_DIM = 2;
277 if (VertexArray::ColsAtCompileTime == Eigen::Dynamic) {
280 num_coords = VertexArray::ColsAtCompileTime;
282 if (FacetArray::ColsAtCompileTime == Eigen::Dynamic) {
283 max_face_size = size_t(loader.max_face_size);
284 if (!loader.is_face_size_constant && loader.params.triangulate) {
288 max_face_size = size_t(FacetArray::ColsAtCompileTime);
291 if (loader.params.as_one_mesh && !loader.is_last_object) {
296 if (loader.vertices.size() == 0) {
297 loader.object_name = name;
302 if (max_face_size < safe_cast<size_t>(loader.max_face_size)) {
303 loader.triangulate();
306 auto num_faces = loader.face_sizes.size();
307 VertexArray vertices(loader.vertices.size() / DIM, num_coords);
308 FacetArray faces(num_faces, max_face_size);
310 typename MeshType::UVArray uvs;
311 typename MeshType::UVIndices uv_indices;
312 typename MeshType::AttributeArray corner_normals;
313 if (!loader.uvs.empty()) {
314 uvs.resize(loader.uvs.size() / UV_DIM, UV_DIM);
315 uv_indices.resize(num_faces, max_face_size);
317 if (!loader.normals.empty()) {
318 corner_normals.resize(num_faces * max_face_size, num_coords);
322 for (
auto i :
range(loader.vertices.size() / DIM)) {
323 for (
auto k :
range(num_coords)) {
324 vertices(i, k) = safe_cast<typename MeshType::Scalar>(loader.vertices[DIM * i + k]);
329 for (
auto i :
range(loader.uvs.size() / UV_DIM)) {
330 for (
auto k :
range(UV_DIM)) {
331 uvs(i, k) = safe_cast<typename MeshType::Scalar>(loader.uvs[UV_DIM * i + k]);
336 size_t indices_i = 0;
337 for (
auto face_index :
range(loader.face_sizes.size())) {
338 const auto face_size = loader.face_sizes[face_index];
340 for (
auto vertex_in_face :
range(face_size)) {
341 const auto& index = loader.indices[indices_i];
342 faces(face_index, vertex_in_face) =
343 safe_cast<typename MeshType::Index>(index.vertex_index);
345 if (index.normal_index != -1) {
346 const auto row_index = face_index * max_face_size + vertex_in_face;
347 for (
auto k :
range(num_coords)) {
348 corner_normals(row_index, k) = safe_cast<typename MeshType::Scalar>(
349 loader.normals[DIM * index.normal_index + k]);
353 if (!loader.uvs.empty()) {
354 uv_indices(face_index, vertex_in_face) =
355 safe_cast<typename MeshType::Index>(index.texcoord_index);
362 for (
auto pad =
size_t(face_size); pad < size_t(max_face_size); pad++) {
363 faces(face_index, pad) = invalid<typename MeshType::Index>();
364 if (!loader.uvs.empty()) {
365 uv_indices(face_index, pad) = invalid<typename MeshType::UVIndices::Scalar>();
367 if (!loader.normals.empty()) {
368 corner_normals.row(face_index * max_face_size + pad).setZero();
375 if (!loader.uvs.empty()) {
376 mesh->initialize_uv(uvs, uv_indices);
379 map_indexed_attribute_to_corner_attribute(*mesh,
"uv");
382 if (!loader.normals.empty()) {
383 mesh->add_corner_attribute(
"normal");
384 mesh->import_corner_attribute(
"normal", corner_normals);
387 if (loader.result.materials.size() > 0 && loader.material_ids.size() == num_faces) {
389 Eigen::Map<Eigen::VectorXi> map(loader.material_ids.data(), loader.material_ids.size());
390 mesh->add_facet_attribute(
"material_id");
391 mesh->set_facet_attribute(
"material_id", map.cast<
typename MeshType::Scalar>());
394 loader.result.meshes.emplace_back(std::move(mesh));
396 loader.result.mesh_names.push_back(loader.object_name);
398 loader.vertex_offset += loader.vertices.size() / DIM;
399 loader.normal_offset += loader.normals.size() / DIM;
400 loader.uv_offset += loader.uvs.size() / UV_DIM;
402 loader.vertices.clear();
404 loader.normals.clear();
405 loader.indices.clear();
406 loader.face_sizes.clear();
407 loader.material_ids.clear();
408 loader.max_face_size = 0;
409 loader.is_face_size_constant =
true;
410 loader.object_name = name;
413 tinyobj::callback_t callback;
414 callback.vertex_cb = vertex_cb;
415 callback.index_cb = index_cb;
416 callback.object_cb = object_cb;
418 if (params.load_normals) callback.normal_cb = normal_cb;
420 if (params.load_uvs) callback.texcoord_cb = texcoord_cb;
422 if (params.load_materials) {
423 callback.mtllib_cb = mtllib_cb;
424 callback.usemtl_cb = usemtl_cb;
427 std::string warning_message;
428 std::string error_message;
429 result.success = tinyobj::LoadObjWithCallback(
433 params.load_materials ? material_reader :
nullptr,
437 mesh_loader.is_last_object =
true;
438 object_cb(&mesh_loader,
"");
440 if (!error_message.empty()) {
441 logger().error(
"Load mesh warning:\n{}", error_message);
443 if (!warning_message.empty()) {
444 logger().warn(
"Load mesh error:\n{}", warning_message);
447 if (params.normalize) {
455template <
typename MeshType>
456MeshLoaderResult<MeshType> load_mesh_ext(
457 const lagrange::fs::path& filename,
458 const MeshLoaderParams& params = {},
459 tinyobj::MaterialReader* material_reader =
nullptr)
461 lagrange::fs::ifstream stream(filename);
462 if (!stream.good()) {
463 MeshLoaderResult<MeshType> result;
464 logger().error(
"Cannot open file: \"{}\"", filename.string());
465 result.success =
false;
468 tinyobj::MaterialFileReader default_reader(filename.parent_path().string());
469 if (params.load_materials && !material_reader) {
470 material_reader = &default_reader;
472 return load_mesh_ext<MeshType>(stream, params, material_reader);
LA_CORE_API spdlog::logger & logger()
Retrieves the current logger.
Definition: Logger.cpp:40
void normalize_meshes(span< SurfaceMesh< Scalar, Index > * > meshes, const TransformOptions &options={})
Normalize a list of meshes to fit in a unit box centered at the origin.
Definition: normalize_meshes.cpp:102
internal::Range< Index > range(Index end)
Returns an iterable object representing the range [0, end).
Definition: range.h:176
Mesh input/output.
Definition: detect_file_format.h:18
auto create_mesh(const Eigen::MatrixBase< DerivedV > &vertices, const Eigen::MatrixBase< DerivedF > &facets)
This function create a new mesh given the vertex and facet arrays by copying data into the Mesh objec...
Definition: create_mesh.h:39
MeshTrait class provide compiler check for different mesh types.
Definition: MeshTrait.h:108
Definition: load_mesh_ext.h:35
bool as_one_mesh
Combines individual objects into a single mesh. Result contains a vector of size 1.
Definition: load_mesh_ext.h:52
bool triangulate
When loading a mesh with mixed facet sizes, this parameter controls whether the polygonal faces will ...
Definition: load_mesh_ext.h:42
bool normalize
Normalize each object to a unit box around the origin.
Definition: load_mesh_ext.h:45
Definition: load_mesh_ext.h:57