ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
TriangleMeshIO.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
9 
10 #include <FileSystem.h>
11 #include <Logging.h>
12 
13 #include <set>
14 #include <unordered_map>
15 
17 
18 namespace cloudViewer {
19 namespace t {
20 namespace io {
21 
22 static const std::unordered_map<
23  std::string,
24  std::function<bool(const std::string &,
28  {"npz", ReadTriangleMeshFromNPZ},
35  };
36 
37 static const std::unordered_map<
38  std::string,
39  std::function<bool(const std::string &,
40  const geometry::TriangleMesh &,
41  const bool,
42  const bool,
43  const bool,
44  const bool,
45  const bool,
46  const bool)>>
48  {"npz", WriteTriangleMeshToNPZ},
50  };
51 
52 std::shared_ptr<geometry::TriangleMesh> CreateMeshFromFile(
53  const std::string &filename, bool print_progress) {
54  auto mesh = std::make_shared<geometry::TriangleMesh>();
56  opt.print_progress = print_progress;
57  ReadTriangleMesh(filename, *mesh, opt);
58  return mesh;
59 }
60 
61 // TODO:
62 // 1. Currently, the tensor triangle mesh implementation has no provision for
63 // triangle_uvs, materials, triangle_material_ids and textures which are
64 // supported by the legacy. These can be added as custom attributes (level 2)
65 // approach. Please check legacy file formats(e.g. FileOBJ.cpp) for more
66 // information.
67 // 2. Add these properties to the legacy to tensor mesh and tensor to legacy
68 // mesh conversion.
69 // 3. Update the documentation with information on how to access these
70 // additional attributes from tensor based triangle mesh.
71 // 4. Implement read/write tensor triangle mesh with various file formats.
72 // 5. Compare with legacy triangle mesh and add corresponding unit tests.
73 
74 bool ReadTriangleMesh(const std::string &filename,
77  std::string filename_ext =
79  if (filename_ext.empty()) {
81  "Read geometry::TriangleMesh failed: unknown file "
82  "extension.");
83  return false;
84  }
85 
86  auto map_itr =
88  bool success = false;
89  if (map_itr == file_extension_to_trianglemesh_read_function.end()) {
90  ccMesh legacy_mesh;
91  success = cloudViewer::io::ReadTriangleMesh(filename, legacy_mesh,
92  params);
93  if (!success) {
94  return false;
95  }
96  mesh = geometry::TriangleMesh::FromLegacy(legacy_mesh);
97  } else {
98  success = map_itr->second(filename, mesh, params);
100  "Read geometry::TriangleMesh: {:d} triangles and {:d} "
101  "vertices.",
102  mesh.GetTriangleIndices().GetLength(),
103  mesh.GetVertexPositions().GetLength());
104  if (mesh.HasVertexPositions() && !mesh.HasTriangleIndices()) {
106  "geometry::TriangleMesh appears to be a "
107  "geometry::PointCloud "
108  "(only contains vertices, but no triangles).");
109  }
110  }
111  return success;
112 }
113 
114 bool WriteTriangleMesh(const std::string &filename,
115  const geometry::TriangleMesh &mesh,
116  bool write_ascii /* = false*/,
117  bool compressed /* = false*/,
118  bool write_vertex_normals /* = true*/,
119  bool write_vertex_colors /* = true*/,
120  bool write_triangle_uvs /* = true*/,
121  bool print_progress /* = false*/) {
122  std::string filename_ext =
124  if (filename_ext.empty()) {
126  "Write geometry::TriangleMesh failed: unknown file "
127  "extension.");
128  return false;
129  }
130  auto map_itr =
132  if (map_itr == file_extension_to_trianglemesh_write_function.end()) {
135  write_vertex_normals, write_vertex_colors, write_triangle_uvs,
136  print_progress);
137  }
138  bool success = map_itr->second(filename, mesh, write_ascii, compressed,
139  write_vertex_normals, write_vertex_colors,
140  write_triangle_uvs, print_progress);
142  "Write geometry::TriangleMesh: {:d} triangles and {:d} vertices.",
143  mesh.GetTriangleIndices().GetLength(),
144  mesh.GetVertexPositions().GetLength());
145  return success;
146 }
147 
149  const std::string &filename,
152  auto attribute_map = ReadNpz(filename);
153 
154  // At a minimum there should be 'vertices' and 'triangles'
155  if (!(attribute_map.count("vertices") > 0) ||
156  !(attribute_map.count("triangles") > 0)) {
158  "Read geometry::TriangleMesh failed: Could not find 'vertices' "
159  "or 'triangles' attributes in {}",
160  filename);
161  return false;
162  }
163 
164  // Fill mesh with attributes
165  for (auto &attr : attribute_map) {
166  if (attr.first == "vertices") {
167  mesh.SetVertexPositions(attr.second);
168  } else if (attr.first == "triangles") {
169  mesh.SetTriangleIndices(attr.second);
170  } else if (attr.first == "vertex_normals") {
171  mesh.SetVertexNormals(attr.second);
172  } else if (attr.first == "triangle_normals") {
173  mesh.SetTriangleNormals(attr.second);
174  } else if (attr.first == "vertex_colors") {
175  mesh.SetVertexColors(attr.second);
176  } else if (attr.first == "triangle_colors") {
177  mesh.SetTriangleColors(attr.second);
178  } else if (attr.first == "triangle_texture_uvs") {
179  mesh.SetTriangleAttr("texture_uvs", attr.second);
180  } else if (attr.first.find("tex_") != std::string::npos) {
181  // Get texture map
182  auto key = attr.first.substr(4);
183  if (!mesh.GetMaterial().IsValid()) {
185  }
186  mesh.GetMaterial().SetTextureMap(key, geometry::Image(attr.second));
187  // Note: due to quirk of CloudViewer shader implementation if we
188  // have a metallic texture we need to set the metallic scalar
189  // propert to 1.0
190  if (key == "metallic") {
191  mesh.GetMaterial().SetScalarProperty("metallic", 1.0);
192  }
193  } else if (attr.first.find("vertex_") != std::string::npos) {
194  // Generic vertex attribute
195  auto key = attr.first.substr(7);
196  mesh.SetVertexAttr(key, attr.second);
197  } else if (attr.first.find("triangle_") != std::string::npos) {
198  // Generic triangle attribute
199  auto key = attr.first.substr(9);
200  mesh.SetTriangleAttr(key, attr.second);
201  } else if (attr.first == "material_name") {
202  if (!mesh.GetMaterial().IsValid()) {
204  }
205  const uint8_t *str_ptr = attr.second.GetDataPtr<uint8_t>();
206  std::string mat_name(attr.second.GetShape().GetLength(), 'a');
207  std::memcpy((void *)mat_name.data(), str_ptr,
208  attr.second.GetShape().GetLength());
209  mesh.GetMaterial().SetMaterialName(mat_name);
210  }
211  }
212 
213  return true;
214 }
215 
216 bool WriteTriangleMeshToNPZ(const std::string &filename,
217  const geometry::TriangleMesh &mesh,
218  const bool write_ascii,
219  const bool compressed,
220  const bool write_vertex_normals,
221  const bool write_vertex_colors,
222  const bool write_triangle_uvs,
223  const bool print_progress) {
224  // Sanity checks...
225  if (write_ascii) {
227  "TriangleMesh can't be saved in ASCII fromat as .npz");
228  return false;
229  }
230  if (compressed) {
232  "TriangleMesh can't be saved in compressed format as .npz");
233  return false;
234  }
235 
236  // Map attribute names to names already used by convention in other software
237  std::set<std::string> known_attributes(
238  {"positions", "normals", "texture_uvs", "indices", "colors"});
239 
240  // Build map of known attributes
241  std::unordered_map<std::string, core::Tensor> mesh_attributes;
242  if (mesh.HasVertexPositions()) {
243  mesh_attributes["vertices"] = mesh.GetVertexPositions();
244  }
245  if (mesh.HasVertexNormals()) {
246  mesh_attributes["vertex_normals"] = mesh.GetVertexNormals();
247  }
248  if (mesh.HasVertexColors()) {
249  mesh_attributes["vertex_colors"] = mesh.GetVertexColors();
250  }
251  if (mesh.HasTriangleIndices()) {
252  mesh_attributes["triangles"] = mesh.GetTriangleIndices();
253  }
254  if (mesh.HasTriangleNormals()) {
255  mesh_attributes["triangle_normals"] = mesh.GetTriangleNormals();
256  }
257  if (mesh.HasTriangleColors()) {
258  mesh_attributes["triangle_colors"] = mesh.GetTriangleColors();
259  }
260  if (mesh.HasTriangleAttr("texture_uvs")) {
261  mesh_attributes["triangle_texture_uvs"] =
262  mesh.GetTriangleAttr("texture_uvs");
263  }
264 
265  // Add "generic" attributes
266  for (auto attr : mesh.GetVertexAttr()) {
267  if (known_attributes.count(attr.first) > 0) {
268  continue;
269  }
270  std::string key_name("vertex_");
271  key_name += attr.first;
272  mesh_attributes[key_name] = attr.second;
273  }
274  for (auto attr : mesh.GetTriangleAttr()) {
275  if (known_attributes.count(attr.first) > 0) {
276  continue;
277  }
278  std::string key_name("triangle_");
279  key_name += attr.first;
280  mesh_attributes[key_name] = attr.second;
281  }
282 
283  // Output texture maps
284  if (mesh.GetMaterial().IsValid()) {
285  auto &mat = mesh.GetMaterial();
286  // Get material name in Tensor form
287  std::vector<uint8_t> mat_name_vec(
288  {mat.GetMaterialName().begin(), mat.GetMaterialName().end()});
289  core::Tensor mat_name_tensor(std::move(mat_name_vec));
290  mesh_attributes["material_name"] = mat_name_tensor;
291  for (auto &tex : mat.GetTextureMaps()) {
292  std::string key = std::string("tex_") + tex.first;
293  mesh_attributes[key] = tex.second.AsTensor();
294  }
295  }
296 
297  WriteNpz(filename, mesh_attributes);
298 
299  return true;
300 }
301 
302 } // namespace io
303 } // namespace t
304 } // namespace cloudViewer
IsAscii write_ascii
Compressed compressed
std::string filename
cmdLineReadable * params[]
Triangular mesh.
Definition: ecvMesh.h:35
int64_t GetLength() const
Definition: Tensor.h:1125
visualization::rendering::Material & GetMaterial()
Get material associated with this Geometry.
The Image class stores image with customizable rows, cols, channels, dtype and device.
Definition: Image.h:29
A triangle mesh contains vertices and triangles.
Definition: TriangleMesh.h:98
void SetTriangleNormals(const core::Tensor &value)
Definition: TriangleMesh.h:298
static geometry::TriangleMesh FromLegacy(const ccMesh &mesh_legacy, core::Dtype float_dtype=core::Float32, core::Dtype int_dtype=core::Int64, const core::Device &device=core::Device("CPU:0"))
const TensorMap & GetVertexAttr() const
Getter for vertex_attr_ TensorMap. Used in Pybind.
Definition: TriangleMesh.h:133
void SetVertexPositions(const core::Tensor &value)
Definition: TriangleMesh.h:261
void SetTriangleIndices(const core::Tensor &value)
Set the value of the "indices" attribute in triangle_attr_.
Definition: TriangleMesh.h:291
ccMesh ToLegacy() const
Convert to a legacy CloudViewer TriangleMesh.
void SetVertexNormals(const core::Tensor &value)
Definition: TriangleMesh.h:275
bool HasTriangleAttr(const std::string &key) const
Definition: TriangleMesh.h:343
void SetVertexAttr(const std::string &key, const core::Tensor &value)
Definition: TriangleMesh.h:254
void SetVertexColors(const core::Tensor &value)
Definition: TriangleMesh.h:268
void SetTriangleColors(const core::Tensor &value)
Definition: TriangleMesh.h:305
void SetTriangleAttr(const std::string &key, const core::Tensor &value)
Definition: TriangleMesh.h:285
const TensorMap & GetTriangleAttr() const
Getter for triangle_attr_ TensorMap. Used in Pybind.
Definition: TriangleMesh.h:159
void SetMaterialName(const std::string &material_name)
Set material name. The material name should match the name of a built.
Definition: Material.h:130
void SetScalarProperty(const std::string &key, float value)
Definition: Material.h:111
void SetTextureMap(const std::string &key, const t::geometry::Image &image)
Definition: Material.cpp:34
#define LogWarning(...)
Definition: Logging.h:72
#define LogDebug(...)
Definition: Logging.h:90
bool WriteTriangleMesh(const std::string &filename, const ccMesh &mesh, bool write_ascii, bool compressed, bool write_vertex_normals, bool write_vertex_colors, bool write_triangle_uvs, bool print_progress)
bool ReadTriangleMesh(const std::string &filename, ccMesh &mesh, ReadTriangleMeshOptions params)
bool ReadTriangleMesh(const std::string &filename, geometry::TriangleMesh &mesh, cloudViewer::io::ReadTriangleMeshOptions params)
std::unordered_map< std::string, core::Tensor > ReadNpz(const std::string &file_name)
Definition: NumpyIO.cpp:675
bool WriteTriangleMeshToNPZ(const std::string &filename, const geometry::TriangleMesh &mesh, const bool write_ascii, const bool compressed, const bool write_vertex_normals, const bool write_vertex_colors, const bool write_triangle_uvs, const bool print_progress)
std::shared_ptr< geometry::TriangleMesh > CreateMeshFromFile(const std::string &filename, bool print_progress)
static const std::unordered_map< std::string, std::function< bool(const std::string &, geometry::TriangleMesh &, const cloudViewer::io::ReadTriangleMeshOptions &)> > file_extension_to_trianglemesh_read_function
void WriteNpz(const std::string &file_name, const std::unordered_map< std::string, core::Tensor > &tensor_map)
Definition: NumpyIO.cpp:759
bool ReadTriangleMeshFromNPZ(const std::string &filename, geometry::TriangleMesh &mesh, const cloudViewer::io::ReadTriangleMeshOptions &params)
static const std::unordered_map< std::string, std::function< bool(const std::string &, const geometry::TriangleMesh &, const bool, const bool, const bool, const bool, const bool, const bool)> > file_extension_to_trianglemesh_write_function
bool WriteTriangleMesh(const std::string &filename, const geometry::TriangleMesh &mesh, bool write_ascii, bool compressed, bool write_vertex_normals, bool write_vertex_colors, bool write_triangle_uvs, bool print_progress)
bool WriteTriangleMeshUsingASSIMP(const std::string &filename, const geometry::TriangleMesh &mesh, const bool write_ascii, const bool compressed, const bool write_vertex_normals, const bool write_vertex_colors, const bool write_triangle_uvs, const bool print_progress)
Definition: FileASSIMP.cpp:371
bool ReadTriangleMeshUsingASSIMP(const std::string &filename, geometry::TriangleMesh &mesh, const cloudViewer::io::ReadTriangleMeshOptions &params)
Definition: FileASSIMP.cpp:46
std::string GetFileExtensionInLowerCase(const std::string &filename)
Definition: FileSystem.cpp:281
Generic file read and write utility for python interface.