Tesseract
Motion Planning Environment
Loading...
Searching...
No Matches
mesh_parser.h
Go to the documentation of this file.
1/*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2008, Willow Garage, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 * * Neither the name of the Willow Garage nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *********************************************************************/
34
35/* Author: Ioan Sucan */
36
37/* Updated by John Wason, Wason Technology, LLC, Dec 2020. Add loading mesh normals,
38 materials, and textures */
39
40#ifndef TESSERACT_SCENE_GRAPH_MESH_PARSER_H
41#define TESSERACT_SCENE_GRAPH_MESH_PARSER_H
42
45#include <fstream>
46
47#include <assimp/scene.h>
48#include <assimp/Importer.hpp>
49#include <assimp/postprocess.h>
50
51#ifdef TESSERACT_ASSIMP_USE_PBRMATERIAL
52#include <assimp/pbrmaterial.h>
53#endif
54
55#include <console_bridge/console.h>
56
59
60#include <regex>
61#include <boost/filesystem/path.hpp>
62
64
66
67namespace tesseract_geometry
68{
75template <class T>
76std::vector<std::shared_ptr<T>> extractMeshData(const aiScene* scene,
77 const aiNode* node,
78 const aiMatrix4x4& parent_transform,
79 const Eigen::Vector3d& scale,
81 bool normals,
82 bool vertex_colors,
83 bool material_and_texture)
84{
85 std::vector<std::shared_ptr<T>> meshes;
86 meshes.reserve(node->mNumMeshes);
87
88 aiMatrix4x4 transform = parent_transform;
89 transform *= node->mTransformation;
90 for (unsigned int j = 0; j < node->mNumMeshes; ++j)
91 {
92 auto vertices = std::make_shared<tesseract_common::VectorVector3d>();
93 auto triangles = std::make_shared<Eigen::VectorXi>();
94 std::shared_ptr<tesseract_common::VectorVector3d> vertex_normals = nullptr;
95 std::shared_ptr<tesseract_common::VectorVector4d> vertex_colors = nullptr;
97 std::shared_ptr<std::vector<MeshTexture::Ptr>> textures = nullptr;
98
99 const aiMesh* a = scene->mMeshes[node->mMeshes[j]];
100 vertices->reserve(a->mNumVertices);
101 for (unsigned int i = 0; i < a->mNumVertices; ++i)
102 {
103 aiVector3D v = transform * a->mVertices[i];
104 vertices->push_back(Eigen::Vector3d(static_cast<double>(v.x) * scale(0),
105 static_cast<double>(v.y) * scale(1),
106 static_cast<double>(v.z) * scale(2)));
107 }
108
109 long triangle_count = 0;
110 std::vector<int> local_triangles;
111 local_triangles.reserve(a->mNumFaces);
112 for (unsigned int i = 0; i < a->mNumFaces; ++i)
113 {
114 if (a->mFaces[i].mNumIndices >= 3)
115 {
116 triangle_count += 1;
117 local_triangles.push_back(static_cast<int>(a->mFaces[i].mNumIndices));
118 for (size_t k = 0; k < a->mFaces[i].mNumIndices; ++k)
119 local_triangles.push_back(static_cast<int>(a->mFaces[i].mIndices[k]));
120 }
121 else
122 {
123 CONSOLE_BRIDGE_logDebug("Mesh had a face with less than three vertices: %s", resource->getUrl().c_str());
124 }
125 }
126
127 triangles->resize(static_cast<long>(local_triangles.size()));
128 for (long i = 0; i < triangles->size(); ++i)
129 (*triangles)[i] = local_triangles[static_cast<size_t>(i)];
130
131 if (normals && a->HasNormals())
132 {
133 vertex_normals = std::make_shared<tesseract_common::VectorVector3d>();
134 vertex_normals->reserve(a->mNumVertices);
135 for (unsigned int i = 0; i < a->mNumVertices; ++i)
136 {
137 aiVector3D v = transform * a->mNormals[i];
138 vertex_normals->push_back(Eigen::Vector3d(static_cast<double>(v.x) * scale(0),
139 static_cast<double>(v.y) * scale(1),
140 static_cast<double>(v.z) * scale(2)));
141 }
142 }
143
144 if (vertex_colors && a->HasVertexColors(0))
145 {
146 vertex_colors = std::make_shared<tesseract_common::VectorVector4d>();
147 vertex_colors->reserve(a->mNumVertices);
148 for (unsigned int i = 0; i < a->mNumVertices; ++i)
149 {
150 aiColor4D v = a->mColors[0][i];
151 vertex_colors->push_back(Eigen::Vector4d(
152 static_cast<double>(v.r), static_cast<double>(v.g), static_cast<double>(v.b), static_cast<double>(v.a)));
153 }
154 }
155
156 if (material_and_texture)
157 {
158 aiMaterial* mat = scene->mMaterials[a->mMaterialIndex];
159 {
160 Eigen::Vector4d base_color;
161 double metallic = 0.0;
162 double roughness = 0.5;
163 Eigen::Vector4d emissive;
164
165 aiColor4D pbr_base_color;
166#ifdef TESSERACT_ASSIMP_USE_PBRMATERIAL
167 if (mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR, pbr_base_color) == AI_SUCCESS)
168 {
169 // Use PBR Metallic material properties if available
170 base_color = Eigen::Vector4d(pbr_base_color.r, pbr_base_color.g, pbr_base_color.b, pbr_base_color.a);
171 float metallicFactor{ 0 };
172 if (mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, metallicFactor) == AI_SUCCESS)
173 {
174 metallic = metallicFactor;
175 }
176 float roughnessFactor{ 0.5 };
177 if (mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, roughnessFactor) == AI_SUCCESS)
178 {
179 roughness = roughnessFactor;
180 }
181 aiColor4D pbr_emissive_color;
182 if (mat->Get(AI_MATKEY_COLOR_EMISSIVE, pbr_emissive_color) == AI_SUCCESS)
183 {
184 emissive =
185 Eigen::Vector4d(pbr_emissive_color.r, pbr_emissive_color.g, pbr_emissive_color.b, pbr_emissive_color.a);
186 }
187 }
188 else
189#endif
190 {
191 // Use old style material. Ambient and specular not supported
192 aiColor4D diffuse_color;
193 if (mat->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse_color) == AI_SUCCESS)
194 {
195 base_color = Eigen::Vector4d(diffuse_color.r, diffuse_color.g, diffuse_color.b, diffuse_color.a);
196 }
197
198 aiColor4D emissive_color;
199 if (mat->Get(AI_MATKEY_COLOR_EMISSIVE, emissive_color) == AI_SUCCESS)
200 {
201 emissive = Eigen::Vector4d(emissive_color.r, emissive_color.g, emissive_color.b, emissive_color.a);
202 }
203 }
204
205 material = std::make_shared<MeshMaterial>(base_color, metallic, roughness, emissive);
206
207 for (unsigned int i = 0; i < a->GetNumUVChannels(); i++)
208 {
209 if (a->HasTextureCoords(i))
210 {
211 aiString texName;
212 aiTextureMapping mapping{ aiTextureMapping_OTHER };
213 unsigned int uvIndex{ 0 };
214 if (mat->GetTexture(aiTextureType_DIFFUSE, i, &texName, &mapping, &uvIndex) == AI_SUCCESS)
215 {
218 // https://stackoverflow.com/questions/56820244/assimp-doenst-return-texture-data
219 const char* texNamec = texName.C_Str();
220 if ('*' == *texNamec)
221 {
222 int tex_index = std::atoi(texNamec + 1);
223 if (0 > tex_index || scene->mNumTextures <= static_cast<unsigned>(tex_index))
224 continue;
225 auto* texture_data = scene->mTextures[tex_index];
226 // returned pointer is not null, read texture from memory
227 std::string file_type = texture_data->achFormatHint;
228 if (file_type == "jpg" || file_type == "png")
229 {
230 texture_image = std::make_shared<tesseract_common::BytesResource>(
231 "data://", (const uint8_t*)texture_data->pcData, texture_data->mWidth); // NOLINT
232 }
233 else
234 {
235 // TODO: handle other file types
236 continue;
237 }
238 }
239 else
240 {
241 if (!resource)
242 {
243 continue;
244 }
245 std::string texName_str(texName.C_Str());
246 auto tex_resource = resource->locateResource(texName_str);
247 if (!tex_resource)
248 {
249 continue;
250 }
251 texture_image = tex_resource;
252 }
253 aiVector3D* tex_coords = a->mTextureCoords[i];
254 for (unsigned int j = 0; j < a->mNumVertices; ++j)
255 {
256 aiVector3D v = tex_coords[j];
257 uvs.push_back(Eigen::Vector2d(static_cast<double>(v.x), static_cast<double>(v.y)));
258 }
259 auto tex = std::make_shared<MeshTexture>(
260 texture_image, std::make_shared<tesseract_common::VectorVector2d>(std::move(uvs)));
261 if (!textures)
262 {
263 textures = std::make_shared<std::vector<MeshTexture::Ptr>>();
264 }
265 textures->push_back(tex);
266 }
267 }
268 }
269 }
270 }
271
272 meshes.push_back(std::make_shared<T>(vertices,
273 triangles,
274 static_cast<int>(triangle_count),
275 resource,
276 scale,
277 vertex_normals,
278 vertex_colors,
279 material,
280 textures));
281 }
282
283 for (unsigned int n = 0; n < node->mNumChildren; ++n)
284 {
285 std::vector<std::shared_ptr<T>> child_meshes = extractMeshData<T>(
286 scene, node->mChildren[n], transform, scale, resource, normals, vertex_colors, material_and_texture);
287 meshes.insert(meshes.end(), child_meshes.begin(), child_meshes.end());
288 }
289 return meshes;
290}
291
302template <class T>
303std::vector<std::shared_ptr<T>> createMeshFromAsset(const aiScene* scene,
304 const Eigen::Vector3d& scale,
306 bool normals,
307 bool vertex_colors,
308 bool material_and_texture)
309{
310 if (!scene->HasMeshes())
311 {
312 CONSOLE_BRIDGE_logWarn("Assimp reports scene in %s has no meshes", resource->getUrl().c_str());
313 return std::vector<std::shared_ptr<T>>();
314 }
315 std::vector<std::shared_ptr<T>> meshes = extractMeshData<T>(
316 scene, scene->mRootNode, aiMatrix4x4(), scale, resource, normals, vertex_colors, material_and_texture);
317 if (meshes.empty())
318 {
319 CONSOLE_BRIDGE_logWarn("There are no meshes in the scene %s", resource->getUrl().c_str());
320 return std::vector<std::shared_ptr<T>>();
321 }
322
323 return meshes;
324}
325
339template <class T>
340std::vector<std::shared_ptr<T>> createMeshFromPath(const std::string& path,
341 Eigen::Vector3d scale = Eigen::Vector3d(1, 1, 1),
342 bool triangulate = false,
343 bool flatten = false,
344 bool normals = false,
345 bool vertex_colors = false,
346 bool material_and_texture = false)
347{
348 // Create an instance of the Importer class
349 Assimp::Importer importer;
350
351 // Issue #38 fix: as part of the post-processing, we remove all other components in file but
352 // the meshes, as anyway the resulting shapes:Mesh object just receives vertices and triangles.
353 // John Wason Jan 2021 - Adjust flags based on normals, vertex_colors, and material_and_texture parameters
354 unsigned int ai_config_pp_rcv_flags = aiComponent_TANGENTS_AND_BITANGENTS | aiComponent_BONEWEIGHTS |
355 aiComponent_ANIMATIONS | aiComponent_LIGHTS | aiComponent_CAMERAS;
356 if (!normals)
357 {
358 ai_config_pp_rcv_flags |= aiComponent_NORMALS;
359 }
360 if (!vertex_colors)
361 {
362 ai_config_pp_rcv_flags |= aiComponent_COLORS;
363 }
364 if (!material_and_texture)
365 {
366 ai_config_pp_rcv_flags |= aiComponent_TEXCOORDS | aiComponent_TEXTURES | aiComponent_MATERIALS;
367 }
368 importer.SetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS, (int)ai_config_pp_rcv_flags);
369
370 // And have it read the given file with some post-processing
371 const aiScene* scene = nullptr;
372 if (triangulate)
373 scene = importer.ReadFile(path.c_str(),
374 aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType |
375 aiProcess_RemoveComponent);
376 else
377 scene = importer.ReadFile(path.c_str(),
378 aiProcess_JoinIdenticalVertices | aiProcess_SortByPType | aiProcess_RemoveComponent);
379
380 if (!scene)
381 {
382 CONSOLE_BRIDGE_logError("Could not load mesh from \"%s\": %s", path.c_str(), importer.GetErrorString());
383 return std::vector<std::shared_ptr<T>>();
384 }
385
386 // Assimp enforces Y_UP convention by rotating models with different conventions.
387 // However, that behavior is confusing and doesn't match the ROS convention
388 // where the Z axis is pointing up.
389 // Hopefully this doesn't undo legit use of the root node transformation...
390 // Note that this is also what RViz does internally.
391 // @todo See this issue for possible fix parsing scene metadata https://github.com/assimp/assimp/issues/849
392 scene->mRootNode->mTransformation = aiMatrix4x4();
393
394 if (flatten)
395 {
396 // These post processing steps flatten the root node transformation into child nodes,
397 // so they must be delayed until after clearing the root node transform above.
398 importer.ApplyPostProcessing(aiProcess_OptimizeMeshes | aiProcess_OptimizeGraph);
399 }
400 else
401 {
402 importer.ApplyPostProcessing(aiProcess_OptimizeGraph);
403 }
404
405 return createMeshFromAsset<T>(scene, scale, nullptr, normals, vertex_colors, material_and_texture);
406}
407
421template <class T>
423 Eigen::Vector3d scale = Eigen::Vector3d(1, 1, 1),
424 bool triangulate = false,
425 bool flatten = false,
426 bool normals = false,
427 bool vertex_colors = false,
428 bool material_and_texture = false)
429{
430 if (!resource)
431 return std::vector<std::shared_ptr<T>>();
432
433 const char* hint = nullptr;
434 std::string hint_storage;
435
436 std::string resource_url = resource->getUrl();
437 std::regex hint_re("^.*\\.([A-Za-z0-9]{1,8})$");
438 std::smatch hint_match;
439 if (std::regex_match(resource_url, hint_match, hint_re))
440 {
441 if (hint_match.size() == 2)
442 {
443 hint_storage = hint_match[1].str();
444 hint = hint_storage.c_str();
445 }
446 }
447
448 std::vector<uint8_t> data = resource->getResourceContents();
449 if (data.empty())
450 {
451 if (resource->isFile())
452 return createMeshFromPath<T>(
453 resource->getFilePath(), scale, triangulate, flatten, normals, vertex_colors, material_and_texture);
454
455 return std::vector<std::shared_ptr<T>>();
456 }
457
458 // Create an instance of the Importer class
459 Assimp::Importer importer;
460
461 // Issue #38 fix: as part of the post-processing, we remove all other components in file but
462 // the meshes, as anyway the resulting shapes:Mesh object just receives vertices and triangles.
463 // John Wason Jan 2021 - Adjust flags based on normals, vertex_colors, and material_and_texture parameters
464 unsigned int ai_config_pp_rcv_flags = aiComponent_TANGENTS_AND_BITANGENTS | aiComponent_BONEWEIGHTS |
465 aiComponent_ANIMATIONS | aiComponent_LIGHTS | aiComponent_CAMERAS;
466 if (!normals)
467 {
468 ai_config_pp_rcv_flags |= aiComponent_NORMALS;
469 }
470 if (!vertex_colors)
471 {
472 ai_config_pp_rcv_flags |= aiComponent_COLORS;
473 }
474 if (!material_and_texture)
475 {
476 ai_config_pp_rcv_flags |= aiComponent_TEXCOORDS | aiComponent_TEXTURES | aiComponent_MATERIALS;
477 }
478 importer.SetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS, (int)ai_config_pp_rcv_flags);
479
480 // And have it read the given file with some post-processing
481 const aiScene* scene = nullptr;
482 if (triangulate)
483 scene = importer.ReadFileFromMemory(data.data(),
484 data.size(),
485 aiProcess_Triangulate | aiProcess_JoinIdenticalVertices |
486 aiProcess_SortByPType | aiProcess_RemoveComponent,
487 hint);
488 else
489 scene =
490 importer.ReadFileFromMemory(data.data(),
491 data.size(),
492 aiProcess_JoinIdenticalVertices | aiProcess_SortByPType | aiProcess_RemoveComponent,
493 hint);
494
495 if (!scene)
496 {
497 CONSOLE_BRIDGE_logError(
498 "Could not load mesh from \"%s\": %s", resource->getUrl().c_str(), importer.GetErrorString());
499 return std::vector<std::shared_ptr<T>>();
500 }
501
502 // Assimp enforces Y_UP convention by rotating models with different conventions.
503 // However, that behavior is confusing and doesn't match the ROS convention
504 // where the Z axis is pointing up.
505 // Hopefully this doesn't undo legit use of the root node transformation...
506 // Note that this is also what RViz does internally.
507 scene->mRootNode->mTransformation = aiMatrix4x4();
508
509 if (flatten)
510 {
511 // These post processing steps flatten the root node transformation into child nodes,
512 // so they must be delayed until after clearing the root node transform above.
513 importer.ApplyPostProcessing(aiProcess_OptimizeMeshes | aiProcess_OptimizeGraph);
514 }
515 else
516 {
517 importer.ApplyPostProcessing(aiProcess_OptimizeGraph);
518 }
519
520 return createMeshFromAsset<T>(scene, scale, resource, normals, vertex_colors, material_and_texture);
521}
522
538template <typename T>
539static std::vector<std::shared_ptr<T>> createMeshFromBytes(const std::string& url,
540 const uint8_t* bytes,
541 size_t bytes_len,
542 Eigen::Vector3d scale = Eigen::Vector3d(1, 1, 1),
543 bool triangulate = false,
544 bool flatten = false,
545 bool normals = false,
546 bool vertex_colors = false,
547 bool material_and_texture = false)
548{
549 std::shared_ptr<tesseract_common::Resource> resource =
550 std::make_shared<tesseract_common::BytesResource>(url, bytes, bytes_len);
551 return tesseract_geometry::createMeshFromResource<T>(
552 resource, scale, triangulate, flatten, normals, vertex_colors, material_and_texture);
553}
554
555} // namespace tesseract_geometry
556
557#endif
std::shared_ptr< Resource > Ptr
Definition: resource_locator.h:101
std::shared_ptr< MeshMaterial > Ptr
Definition: mesh_material.h:63
results transform[0]
Definition: collision_core_unit.cpp:144
CollisionMarginData data(default_margin)
Definition: collision_margin_data_unit.cpp:34
double scale
Definition: collision_margin_data_unit.cpp:133
Common Tesseract Macros.
#define TESSERACT_COMMON_IGNORE_WARNINGS_PUSH
Definition: macros.h:71
Tesseract Mesh Material read from a mesh file.
Definition: create_convex_hull.cpp:36
AlignedVector< Eigen::Vector2d > VectorVector2d
Definition: types.h:65
Definition: geometry.h:39
std::vector< std::shared_ptr< T > > createMeshFromPath(const std::string &path, Eigen::Vector3d scale=Eigen::Vector3d(1, 1, 1), bool triangulate=false, bool flatten=false, bool normals=false, bool vertex_colors=false, bool material_and_texture=false)
Create a mesh using assimp from file path.
Definition: mesh_parser.h:340
std::vector< std::shared_ptr< T > > createMeshFromAsset(const aiScene *scene, const Eigen::Vector3d &scale, tesseract_common::Resource::Ptr resource, bool normals, bool vertex_colors, bool material_and_texture)
Create list of meshes from the assimp scene.
Definition: mesh_parser.h:303
std::vector< std::shared_ptr< T > > extractMeshData(const aiScene *scene, const aiNode *node, const aiMatrix4x4 &parent_transform, const Eigen::Vector3d &scale, tesseract_common::Resource::Ptr resource, bool normals, bool vertex_colors, bool material_and_texture)
Definition: mesh_parser.h:76
static std::vector< std::shared_ptr< T > > createMeshFromBytes(const std::string &url, const uint8_t *bytes, size_t bytes_len, Eigen::Vector3d scale=Eigen::Vector3d(1, 1, 1), bool triangulate=false, bool flatten=false, bool normals=false, bool vertex_colors=false, bool material_and_texture=false)
Create a mesh from byte array.
Definition: mesh_parser.h:539
std::vector< std::shared_ptr< T > > createMeshFromResource(tesseract_common::Resource::Ptr resource, Eigen::Vector3d scale=Eigen::Vector3d(1, 1, 1), bool triangulate=false, bool flatten=false, bool normals=false, bool vertex_colors=false, bool material_and_texture=false)
Create a mesh using assimp from resource.
Definition: mesh_parser.h:422
Locate and retrieve resource data.
Resource::Ptr resource
Definition: resource_locator_unit.cpp:59
Common Tesseract Types.
v
Definition: tesseract_common_unit.cpp:369
std::vector< Mesh::Ptr > meshes
Definition: tesseract_geometry_unit.cpp:306
JointDynamics j
Definition: tesseract_scene_graph_joint_unit.cpp:15
tesseract_common::fs::path path
Definition: tesseract_srdf_unit.cpp:1992