8 #include "io/TriangleMeshIO.h"
10 #include <Eigen/Geometry>
13 #include <unordered_map>
29 #include <tiny_gltf.h>
30 #include <tiny_obj_loader.h>
39 namespace ply_trianglemesh_reader {
41 struct PLYReaderState {
50 std::vector<unsigned int>
face;
57 int ReadVertexCallback(p_ply_argument argument) {
58 PLYReaderState* state_ptr;
60 ply_get_argument_user_data(argument,
reinterpret_cast<void**
>(&state_ptr),
62 if (state_ptr->vertex_index >= state_ptr->vertex_num) {
66 double value = ply_get_argument_value(argument);
68 state_ptr->mesh_ptr->getAssociatedCloud()->getPoint(
69 static_cast<unsigned int>(state_ptr->vertex_index)));
70 P_ptr->
u[index] = value;
72 state_ptr->vertex_index++;
73 ++(*state_ptr->progress_bar);
78 int ReadNormalCallback(p_ply_argument argument) {
79 PLYReaderState* state_ptr;
81 ply_get_argument_user_data(argument,
reinterpret_cast<void**
>(&state_ptr),
83 if (state_ptr->normal_index >= state_ptr->normal_num) {
87 state_ptr->normals[index] = ply_get_argument_value(argument);
90 state_ptr->mesh_ptr->setVertexNormal(
91 state_ptr->normal_index,
92 Eigen::Vector3d(state_ptr->normals[0], state_ptr->normals[1],
93 state_ptr->normals[2]));
94 state_ptr->normal_index++;
99 int ReadColorCallback(p_ply_argument argument) {
100 PLYReaderState* state_ptr;
102 ply_get_argument_user_data(argument,
reinterpret_cast<void**
>(&state_ptr),
104 if (state_ptr->color_index >= state_ptr->color_num) {
108 double value = ply_get_argument_value(argument);
109 state_ptr->colors[index] = value / 255.0;
112 state_ptr->mesh_ptr->setVertexColor(
113 state_ptr->color_index,
114 Eigen::Vector3d(state_ptr->colors[0], state_ptr->colors[1],
115 state_ptr->colors[2]));
116 state_ptr->color_index++;
121 int ReadFaceCallBack(p_ply_argument argument) {
122 PLYReaderState* state_ptr;
123 long dummy,
length, index;
124 ply_get_argument_user_data(argument,
reinterpret_cast<void**
>(&state_ptr),
126 double value = ply_get_argument_value(argument);
127 if (state_ptr->face_index >= state_ptr->face_num) {
131 ply_get_argument_property(argument,
nullptr, &
length, &index);
133 state_ptr->face.clear();
135 state_ptr->face.push_back(
int(value));
137 if (
long(state_ptr->face.size()) ==
length) {
140 "Read PLY failed: A polygon in the mesh could not be "
141 "decomposed into triangles.");
144 state_ptr->face_index++;
145 ++(*state_ptr->progress_bar);
152 static const std::unordered_map<
155 const std::string&,
ccMesh&,
const ReadTriangleMeshOptions&)>>
168 static const std::unordered_map<std::string,
169 std::function<bool(
const std::string&,
193 bool print_progress) {
194 auto mesh = std::make_shared<ccMesh>();
195 if (!mesh->CreateInternalCloud()) {
209 std::string filename_ext =
211 if (filename_ext.empty()) {
213 "Read ccMesh failed: unknown file "
221 "Read ccMesh failed: unknown file "
226 if (
params.print_progress) {
227 auto progress_text = std::string(
"Reading ") +
231 params.update_progress = [pbar](
double percent)
mutable ->
bool {
232 pbar.setCurrentCount(
size_t(percent));
238 if (!mesh.CreateInternalCloud()) {
249 "ccMesh appears to be a geometry::ccPointCloud "
250 "(only contains vertices, but no triangles).");
259 bool write_vertex_normals ,
260 bool write_vertex_colors ,
261 bool write_triangle_uvs ,
262 bool print_progress ) {
263 std::string filename_ext =
265 if (filename_ext.empty()) {
267 "Write ccMesh failed: unknown file "
275 "Write ccMesh failed: unknown file "
280 write_vertex_normals, write_vertex_colors,
281 write_triangle_uvs, print_progress);
290 for (
int i = 0; i <
polygon.rows(); ++i) {
292 int j = (i + 1) %
polygon.rows();
303 if (((vy0 <= y) && (vy1 > y)) || ((vy1 <= y) && (vy0 > y))) {
305 double cross = (vx1 - vx0) * (y - vy0) / (vy1 - vy0) + vx0;
308 if (
cross < x) inside = !inside;
315 std::vector<unsigned int>& indices) {
316 int n = int(indices.size());
317 Eigen::Vector3d face_normal = Eigen::Vector3d::Zero();
319 for (
int i = 0; i < n; i++) {
320 const Eigen::Vector3d& v1 = mesh.
getVertice(indices[(i + 1) % n]) -
322 const Eigen::Vector3d& v2 = mesh.
getVertice(indices[(i + 2) % n]) -
324 face_normal += v1.cross(v2);
326 double l = std::sqrt(face_normal.dot(face_normal));
327 face_normal *= (1.0 / l);
330 bool found_ear =
true;
339 for (
int i = 1; i < n - 2; i++) {
340 const Eigen::Vector3d& v1 = mesh.
getVertice(indices[i]) -
342 const Eigen::Vector3d& v2 = mesh.
getVertice(indices[i + 1]) -
344 bool is_convex = (face_normal.dot(v1.cross(v2)) > 0.0);
349 Eigen::MatrixX2d
polygon(3, 2);
350 for (
int j = 0; j < 3; j++) {
355 for (
int j = 0; j < n; j++) {
356 if (j == i - 1 || j == i || j == i + 1) {
359 const Eigen::Vector3d& v = mesh.
getVertice(indices[j]);
370 indices.erase(indices.begin() + i);
371 n = int(indices.size());
385 using namespace ply_trianglemesh_reader;
387 p_ply ply_file = ply_open(
filename.c_str(),
nullptr, 0,
nullptr);
393 if (!ply_read_header(ply_file)) {
399 PLYReaderState state;
400 state.mesh_ptr = &mesh;
401 state.vertex_num = ply_set_read_cb(ply_file,
"vertex",
"x",
402 ReadVertexCallback, &state, 0);
403 ply_set_read_cb(ply_file,
"vertex",
"y", ReadVertexCallback, &state, 1);
404 ply_set_read_cb(ply_file,
"vertex",
"z", ReadVertexCallback, &state, 2);
406 state.normal_num = ply_set_read_cb(ply_file,
"vertex",
"nx",
407 ReadNormalCallback, &state, 0);
408 ply_set_read_cb(ply_file,
"vertex",
"ny", ReadNormalCallback, &state, 1);
409 ply_set_read_cb(ply_file,
"vertex",
"nz", ReadNormalCallback, &state, 2);
411 state.color_num = ply_set_read_cb(ply_file,
"vertex",
"red",
412 ReadColorCallback, &state, 0);
413 ply_set_read_cb(ply_file,
"vertex",
"green", ReadColorCallback, &state, 1);
414 ply_set_read_cb(ply_file,
"vertex",
"blue", ReadColorCallback, &state, 2);
416 if (state.vertex_num <= 0) {
422 state.face_num = ply_set_read_cb(ply_file,
"face",
"vertex_indices",
423 ReadFaceCallBack, &state, 0);
424 if (state.face_num == 0) {
425 state.face_num = ply_set_read_cb(ply_file,
"face",
"vertex_index",
426 ReadFaceCallBack, &state, 0);
429 state.vertex_index = 0;
430 state.normal_index = 0;
431 state.color_index = 0;
432 state.face_index = 0;
438 cloud->
resize(state.vertex_num);
439 if (state.normal_num > 0) {
442 if (state.color_num > 0) {
447 reporter.
SetTotal(state.vertex_num + state.face_num);
448 state.progress_bar = &reporter;
450 if (!ply_read(ply_file)) {
465 bool write_vertex_normals ,
466 bool write_vertex_colors ,
467 bool write_triangle_uvs ,
468 bool print_progress) {
471 "This file format currently does not support writing textures "
472 "and uv coordinates. Consider using .obj");
475 if (mesh.
size() == 0) {
480 p_ply ply_file = ply_create(
filename.c_str(),
482 nullptr, 0,
nullptr);
490 write_vertex_colors = write_vertex_colors && mesh.
hasColors();
492 ply_add_comment(ply_file,
"Created by cloudViewer");
493 ply_add_element(ply_file,
"vertex",
498 if (write_vertex_normals) {
503 if (write_vertex_colors) {
508 ply_add_element(ply_file,
"face",
static_cast<long>(mesh.
size()));
510 if (!ply_write_header(ply_file)) {
518 "Writing PLY: ", print_progress);
519 bool printed_color_warning =
false;
522 ply_write(ply_file, vertex(0));
523 ply_write(ply_file, vertex(1));
524 ply_write(ply_file, vertex(2));
525 if (write_vertex_normals) {
527 ply_write(ply_file,
normal(0));
528 ply_write(ply_file,
normal(1));
529 ply_write(ply_file,
normal(2));
531 if (write_vertex_colors) {
533 if (!printed_color_warning &&
537 "Write Ply clamped color value to valid range");
538 printed_color_warning =
true;
541 ply_write(ply_file, rgb(0));
542 ply_write(ply_file, rgb(1));
543 ply_write(ply_file, rgb(2));
547 for (
unsigned int i = 0; i < mesh.
size(); i++) {
550 ply_write(ply_file, 3);
551 ply_write(ply_file, triangle(0));
552 ply_write(ply_file, triangle(1));
553 ply_write(ply_file, triangle(2));
571 bool print_progress) {
580 int num_of_triangles;
582 char header[80] =
"";
583 if (fread(header,
sizeof(
char), 80, myFile) != 80) {
585 "[TriangleMeshIO::ReadTriangleMeshFromSTL] header IO "
588 if (fread(&num_of_triangles,
sizeof(
unsigned int), 1, myFile) != 1) {
590 "[TriangleMeshIO::ReadTriangleMeshFromSTL] triangles IO "
599 if (num_of_triangles == 0) {
607 mesh.
reserve(num_of_triangles);
613 "Reading STL: ", print_progress);
614 for (
int i = 0; i < num_of_triangles; i++) {
618 if (fread(buffer,
sizeof(
char), 50, myFile) != 50) {
620 "[TriangleMeshIO::ReadTriangleMeshFromSTL] buffer IO "
623 float_buffer =
reinterpret_cast<float*
>(buffer);
625 Eigen::Map<Eigen::Vector3f>(float_buffer).cast<
double>());
626 for (
int j = 0; j < 3; j++) {
627 float_buffer =
reinterpret_cast<float*
>(buffer + 12 * (j + 1));
628 Eigen::Vector3d temp = Eigen::Map<Eigen::Vector3f>(float_buffer)
630 *cloud->
getPointPtr(
static_cast<unsigned int>(i * 3 + j)) =
666 bool write_vertex_normals ,
667 bool write_vertex_colors ,
668 bool write_triangle_uvs ,
669 bool print_progress) {
672 "This file format does not support writing textures and uv "
673 "coordinates. Consider using .obj");
680 std::ofstream myFile(
filename.c_str(), std::ios::out | std::ios::binary);
692 size_t num_of_triangles = mesh.
size();
693 if (num_of_triangles == 0) {
697 char header[80] =
"Created by cloudViewer";
698 myFile.write(header, 80);
699 myFile.write((
char*)(&num_of_triangles), 4);
702 "Writing STL: ", print_progress);
703 for (
size_t i = 0; i < num_of_triangles; i++) {
704 Eigen::Vector3f float_vector3f =
707 myFile.write(
reinterpret_cast<const char*
>(float_vector3f.data()), 12);
709 std::vector<Eigen::Vector3d> vN3;
711 vN3[1].data(), vN3[2].data());
712 for (
int j = 0; j < 3; j++) {
713 Eigen::Vector3f float_vector3f = vN3[j].cast<
float>();
714 myFile.write(
reinterpret_cast<const char*
>(float_vector3f.data()),
717 char blank[2] = {0, 0};
718 myFile.write(blank, 2);
731 tinyobj::attrib_t attrib;
732 std::vector<tinyobj::shape_t> shapes;
733 std::vector<tinyobj::material_t> materials;
737 std::string mtl_base_path =
739 bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
740 filename.c_str(), mtl_base_path.c_str());
759 static_cast<unsigned int>(attrib.vertices.size()));
761 if (attrib.colors.size() > 0) {
767 for (
size_t vidx = 0; vidx < attrib.vertices.size(); vidx += 3) {
768 tinyobj::real_t vx = attrib.vertices[vidx + 0];
769 tinyobj::real_t vy = attrib.vertices[vidx + 1];
770 tinyobj::real_t vz = attrib.vertices[vidx + 2];
774 for (
size_t vidx = 0; vidx < attrib.colors.size(); vidx += 3) {
775 tinyobj::real_t r = attrib.colors[vidx + 0];
776 tinyobj::real_t g = attrib.colors[vidx + 1];
777 tinyobj::real_t b = attrib.colors[vidx + 2];
782 if (!attrib.normals.empty()) {
785 std::vector<bool> normals_indicator(cloud->
size(),
false);
789 for (
size_t s = 0; s < shapes.size(); s++) {
790 size_t index_offset = 0;
791 for (
size_t f = 0; f < shapes[s].mesh.num_face_vertices.size(); f++) {
792 int fv = shapes[s].mesh.num_face_vertices[f];
795 "Read OBJ failed: facet with number of vertices not "
801 for (
int v = 0; v < fv; v++) {
803 unsigned int vidx = idx.vertex_index;
806 if (!attrib.normals.empty() && !normals_indicator[vidx] &&
807 (3 * idx.normal_index + 2) <
int(attrib.normals.size())) {
809 attrib.normals[3 * idx.normal_index + 0];
811 attrib.normals[3 * idx.normal_index + 1];
813 attrib.normals[3 * idx.normal_index + 2];
816 normals_indicator[vidx] =
true;
819 if (!attrib.texcoords.empty() &&
820 2 * idx.texcoord_index + 1 <
int(attrib.texcoords.size())) {
822 attrib.texcoords[2 * idx.texcoord_index + 0];
824 attrib.texcoords[2 * idx.texcoord_index + 1];
831 shapes[s].mesh.material_ids[f]);
837 bool all_normals_set =
838 std::accumulate(normals_indicator.begin(), normals_indicator.end(),
839 true, [](
bool a,
bool b) { return a && b; });
840 if (!all_normals_set) {
849 auto textureLoader = [&mtl_base_path](std::string& relativePath) {
851 return image->HasData() ?
image : std::shared_ptr<geometry::Image>();
857 for (std::size_t i = 0; i < materials.size(); ++i) {
858 auto& material = materials[i];
860 auto& meshMaterial = mesh.
materials_[i].second;
862 meshMaterial.baseColor = MaterialParameter::CreateRGB(
863 material.diffuse[0], material.diffuse[1], material.diffuse[2]);
865 if (!material.normal_texname.empty()) {
866 meshMaterial.normalMap = textureLoader(material.normal_texname);
867 }
else if (!material.bump_texname.empty()) {
870 meshMaterial.normalMap = textureLoader(material.bump_texname);
873 if (!material.ambient_texname.empty()) {
874 meshMaterial.ambientOcclusion =
875 textureLoader(material.ambient_texname);
878 if (!material.diffuse_texname.empty()) {
879 meshMaterial.albedo = textureLoader(material.diffuse_texname);
882 if (meshMaterial.albedo) {
883 mesh.
textures_.push_back(*meshMaterial.albedo->FlipVertical());
887 if (!material.metallic_texname.empty()) {
888 meshMaterial.metallic = textureLoader(material.metallic_texname);
891 if (!material.roughness_texname.empty()) {
892 meshMaterial.roughness = textureLoader(material.roughness_texname);
895 if (!material.sheen_texname.empty()) {
896 meshMaterial.reflectance = textureLoader(material.sheen_texname);
904 if (material.roughness > 0.f || material.metallic > 0.f) {
905 meshMaterial.baseMetallic = material.metallic;
906 meshMaterial.baseRoughness = material.roughness;
909 if (material.sheen > 0.f) {
910 meshMaterial.baseReflectance = material.sheen;
915 meshMaterial.baseClearCoat = material.clearcoat_thickness;
916 meshMaterial.baseClearCoatRoughness = material.clearcoat_roughness;
917 meshMaterial.baseAnisotropy = material.anisotropy;
927 bool write_vertex_normals ,
928 bool write_vertex_colors ,
929 bool write_triangle_uvs ,
930 bool print_progress) {
934 std::ofstream file(
filename.c_str(), std::ios::out | std::ios::binary);
944 file <<
"# Created by cloudViewer " <<
std::endl;
945 file <<
"# object name: " << object_name <<
std::endl;
947 file <<
"# number of triangles: " << mesh.
size() <<
std::endl;
950 file <<
"mtllib " << object_name <<
".mtl" <<
std::endl;
954 "Writing OBJ: ", print_progress);
956 write_vertex_normals = write_vertex_normals && mesh.
hasNormals();
957 write_vertex_colors = write_vertex_colors && mesh.
hasColors();
959 const Eigen::Vector3d& vertex = mesh.
getVertice(vidx);
960 file <<
"v " << vertex(0) <<
" " << vertex(1) <<
" " << vertex(2);
961 if (write_vertex_colors) {
967 if (write_vertex_normals) {
982 if (write_triangle_uvs) {
990 std::map<int, std::vector<size_t>> material_id_faces_map;
994 auto it = material_id_faces_map.find(mi);
995 if (it == material_id_faces_map.end()) {
996 material_id_faces_map[mi] = {i};
998 it->second.push_back(i);
1002 material_id_faces_map[0].resize(mesh.
size());
1003 std::iota(material_id_faces_map[0].begin(),
1004 material_id_faces_map[0].end(), 0);
1008 for (
auto it = material_id_faces_map.begin();
1009 it != material_id_faces_map.end(); ++it) {
1011 if (write_triangle_uvs) {
1012 std::string mtl_name =
1014 file <<
"usemtl " << mtl_name <<
std::endl;
1018 for (
auto tidx : it->second) {
1021 if (write_vertex_normals && write_triangle_uvs) {
1023 file << triangle(0) + 1 <<
"/" << 3 * tidx + 1 <<
"/"
1024 << triangle(0) + 1 <<
" ";
1025 file << triangle(1) + 1 <<
"/" << 3 * tidx + 2 <<
"/"
1026 << triangle(1) + 1 <<
" ";
1027 file << triangle(2) + 1 <<
"/" << 3 * tidx + 3 <<
"/"
1029 }
else if (!write_vertex_normals && write_triangle_uvs) {
1031 file << triangle(0) + 1 <<
"/" << 3 * tidx + 1 <<
" ";
1032 file << triangle(1) + 1 <<
"/" << 3 * tidx + 2 <<
" ";
1033 file << triangle(2) + 1 <<
"/" << 3 * tidx + 3 <<
std::endl;
1034 }
else if (write_vertex_normals && !write_triangle_uvs) {
1035 file <<
"f " << triangle(0) + 1 <<
"//" << triangle(0) + 1
1036 <<
" " << triangle(1) + 1 <<
"//" << triangle(1) + 1 <<
" "
1037 << triangle(2) + 1 <<
"//" << triangle(2) + 1 <<
std::endl;
1039 file <<
"f " << triangle(0) + 1 <<
" " << triangle(1) + 1 <<
" "
1050 if (write_triangle_uvs) {
1051 std::string parent_dir =
1053 std::string mtl_filename = parent_dir + object_name +
".mtl";
1056 std::ofstream mtl_file(mtl_filename.c_str(), std::ios::out);
1059 "Write OBJ successful, but failed to write material file.");
1062 mtl_file <<
"# Created by cloudViewer " <<
std::endl;
1063 mtl_file <<
"# object name: " << object_name <<
std::endl;
1066 for (
size_t i = 0; i < mesh.
textures_.size(); ++i) {
1068 mtl_file <<
"newmtl " << mtl_name <<
std::endl;
1069 mtl_file <<
"Ka 1.000 1.000 1.000" <<
std::endl;
1070 mtl_file <<
"Kd 1.000 1.000 1.000" <<
std::endl;
1071 mtl_file <<
"Ks 0.000 0.000 0.000" <<
std::endl;
1073 std::string tex_filename = parent_dir + mtl_name +
".png";
1077 "Write OBJ successful, but failed to write texture "
1081 mtl_file <<
"map_Kd " << mtl_name <<
".png\n";
1086 std::string mtl_name = object_name +
"_0";
1087 mtl_file <<
"newmtl " << mtl_name <<
std::endl;
1088 mtl_file <<
"Ka 1.000 1.000 1.000" <<
std::endl;
1089 mtl_file <<
"Kd 1.000 1.000 1.000" <<
std::endl;
1090 mtl_file <<
"Ks 0.000 0.000 0.000" <<
std::endl;
1103 std::ifstream file(
filename.c_str(), std::ios::in);
1110 auto GetNextLine = [](std::ifstream& file) -> std::string {
1111 for (std::string line; std::getline(file, line);) {
1113 if (!line.empty() && line[0] !=
'#') {
1120 std::string header = GetNextLine(file);
1121 if (header !=
"OFF" && header !=
"COFF" && header !=
"NOFF" &&
1122 header !=
"CNOFF") {
1124 "Read OFF failed: header keyword '{}' not supported.", header);
1128 std::string info = GetNextLine(file);
1129 unsigned int num_of_vertices, num_of_faces, num_of_edges;
1130 std::istringstream iss(info);
1131 if (!(iss >> num_of_vertices >> num_of_faces >> num_of_edges)) {
1136 if (num_of_vertices == 0 || num_of_faces == 0) {
1146 cloud->
resize(num_of_vertices);
1147 bool parse_vertex_normals =
false;
1148 bool parse_vertex_colors =
false;
1149 if (header ==
"NOFF" || header ==
"CNOFF") {
1150 parse_vertex_normals =
true;
1153 if (header ==
"COFF" || header ==
"CNOFF") {
1154 parse_vertex_colors =
true;
1159 reporter.
SetTotal(num_of_vertices + num_of_faces);
1163 float r, g, b, alpha;
1164 for (
unsigned int vidx = 0; vidx < num_of_vertices; vidx++) {
1165 std::string line = GetNextLine(file);
1166 std::istringstream iss(line);
1167 if (!(iss >> vx >> vy >> vz)) {
1169 "Read OFF failed: could not read all vertex values.");
1172 *cloud->
getPointPtr(vidx) = Eigen::Vector3d(vx, vy, vz);
1174 if (parse_vertex_normals) {
1175 if (!(iss >> nx >> ny >> nz)) {
1177 "Read OFF failed: could not read all vertex normal "
1183 if (parse_vertex_colors) {
1184 if (!(iss >> r >> g >> b >> alpha)) {
1186 "Read OFF failed: could not read all vertex color "
1200 std::vector<unsigned int> indices;
1201 for (
size_t tidx = 0; tidx < num_of_faces; tidx++) {
1202 std::string line = GetNextLine(file);
1203 std::istringstream iss(line);
1206 for (
size_t vidx = 0; vidx < n; vidx++) {
1209 "Read OFF failed: could not read all vertex "
1217 "Read OFF failed: A polygon in the mesh could not be "
1218 "decomposed into triangles. Vertex indices: {}",
1233 bool write_vertex_normals ,
1234 bool write_vertex_colors ,
1235 bool write_triangle_uvs ,
1236 bool print_progress) {
1239 "This file format does not support writing textures and uv "
1240 "coordinates. Consider using .obj");
1243 std::ofstream file(
filename.c_str(), std::ios::out);
1254 size_t num_of_triangles = mesh.
size();
1255 if (num_of_vertices == 0 || num_of_triangles == 0) {
1260 write_vertex_normals = write_vertex_normals && mesh.
hasNormals();
1261 write_vertex_colors = write_vertex_colors && mesh.
hasColors();
1262 if (write_vertex_colors) {
1265 if (write_vertex_normals) {
1269 file << num_of_vertices <<
" " << num_of_triangles <<
" 0" <<
std::endl;
1272 "Writing OFF: ", print_progress);
1273 for (
size_t vidx = 0; vidx < num_of_vertices; ++vidx) {
1274 const Eigen::Vector3d& vertex = mesh.
getVertice(vidx);
1275 file << vertex(0) <<
" " << vertex(1) <<
" " << vertex(2);
1276 if (write_vertex_normals) {
1280 if (write_vertex_colors) {
1282 file <<
" " << std::round(
color(0) * 255.0) <<
" "
1283 << std::round(
color(1) * 255.0) <<
" "
1284 << std::round(
color(2) * 255.0) <<
" 255";
1290 for (
size_t tidx = 0; tidx < num_of_triangles; ++tidx) {
1293 file <<
"3 " << triangle(0) <<
" " << triangle(1) <<
" " << triangle(2)
1304 template <
typename T>
1323 throw std::out_of_range(
1324 "Tried to access beyond the last element of an array "
1325 "adapter with count " +
1359 tinygltf::Model model;
1360 tinygltf::TinyGLTF loader;
1364 std::string filename_ext =
1367 if (filename_ext ==
"glb") {
1368 ret = loader.LoadBinaryFromFile(&model, &err, &warn,
filename.c_str());
1370 ret = loader.LoadASCIIFromFile(&model, &err, &warn,
filename.c_str());
1373 if (!warn.empty() || !err.empty()) {
1381 if (model.meshes.size() > 1) {
1383 "The file contains more than one mesh. All meshes will be "
1384 "loaded as a single mesh.");
1391 ccMesh mesh_temp(baseVertice);
1394 for (
const tinygltf::Node& gltf_node : model.nodes) {
1395 if (gltf_node.mesh != -1) {
1397 const tinygltf::Mesh& gltf_mesh = model.meshes[gltf_node.mesh];
1399 for (
const tinygltf::Primitive& primitive : gltf_mesh.primitives) {
1400 for (
const auto& attribute : primitive.attributes) {
1401 if (attribute.first ==
"POSITION") {
1402 tinygltf::Accessor& positions_accessor =
1403 model.accessors[attribute.second];
1404 tinygltf::BufferView& positions_view =
1405 model.bufferViews[positions_accessor
1407 const tinygltf::Buffer& positions_buffer =
1408 model.buffers[positions_view.buffer];
1409 const float* positions =
reinterpret_cast<const float*
>(
1411 .data[positions_view.byteOffset +
1412 positions_accessor.byteOffset]);
1414 for (
size_t i = 0; i < positions_accessor.count; ++i) {
1416 positions[i * 3 + 0], positions[i * 3 + 1],
1417 positions[i * 3 + 2]));
1421 if (attribute.first ==
"NORMAL") {
1422 tinygltf::Accessor& normals_accessor =
1423 model.accessors[attribute.second];
1424 tinygltf::BufferView& normals_view =
1425 model.bufferViews[normals_accessor.bufferView];
1426 const tinygltf::Buffer& normals_buffer =
1427 model.buffers[normals_view.buffer];
1428 const float*
normals =
reinterpret_cast<const float*
>(
1430 .data[normals_view.byteOffset +
1431 normals_accessor.byteOffset]);
1433 for (
size_t i = 0; i < normals_accessor.count; ++i) {
1440 if (attribute.first ==
"COLOR_0") {
1441 tinygltf::Accessor& colors_accessor =
1442 model.accessors[attribute.second];
1443 tinygltf::BufferView& colors_view =
1444 model.bufferViews[colors_accessor.bufferView];
1445 const tinygltf::Buffer& colors_buffer =
1446 model.buffers[colors_view.buffer];
1448 size_t byte_stride = colors_view.byteStride;
1449 if (byte_stride == 0) {
1454 colors_accessor.type *
1455 tinygltf::GetComponentSizeInBytes(
1456 colors_accessor.componentType);
1458 switch (colors_accessor.componentType) {
1459 case TINYGLTF_COMPONENT_TYPE_FLOAT: {
1460 for (
size_t i = 0; i < colors_accessor.count;
1463 reinterpret_cast<const float*
>(
1464 colors_buffer.data.data() +
1465 colors_view.byteOffset +
1477 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: {
1478 double max_val = (double)
1480 for (
size_t i = 0; i < colors_accessor.count;
1483 reinterpret_cast<const uint8_t*
>(
1484 colors_buffer.data.data() +
1485 colors_view.byteOffset +
1498 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: {
1499 double max_val = (double)
1501 for (
size_t i = 0; i < colors_accessor.count;
1504 reinterpret_cast<const uint16_t*
>(
1505 colors_buffer.data.data() +
1506 colors_view.byteOffset +
1520 "Unrecognized component type for "
1529 std::unique_ptr<IntArrayBase> indices_array_pointer =
nullptr;
1531 const tinygltf::Accessor& indices_accessor =
1532 model.accessors[primitive.indices];
1533 const tinygltf::BufferView& indices_view =
1534 model.bufferViews[indices_accessor.bufferView];
1535 const tinygltf::Buffer& indices_buffer =
1536 model.buffers[indices_view.buffer];
1537 const auto data_address = indices_buffer.data.data() +
1538 indices_view.byteOffset +
1539 indices_accessor.byteOffset;
1540 const auto byte_stride =
1541 indices_accessor.ByteStride(indices_view);
1542 const auto count = indices_accessor.count;
1546 switch (indices_accessor.componentType) {
1547 case TINYGLTF_COMPONENT_TYPE_BYTE:
1548 indices_array_pointer =
1549 std::unique_ptr<IntArray<char>>(
1552 data_address,
count,
1555 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
1556 indices_array_pointer =
1557 std::unique_ptr<IntArray<unsigned char>>(
1560 data_address,
count,
1563 case TINYGLTF_COMPONENT_TYPE_SHORT:
1564 indices_array_pointer =
1565 std::unique_ptr<IntArray<short>>(
1568 data_address,
count,
1571 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
1572 indices_array_pointer = std::unique_ptr<
1576 data_address,
count,
1579 case TINYGLTF_COMPONENT_TYPE_INT:
1580 indices_array_pointer =
1581 std::unique_ptr<IntArray<int>>(
1583 data_address,
count,
1586 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
1587 indices_array_pointer =
1588 std::unique_ptr<IntArray<unsigned int>>(
1591 data_address,
count,
1597 const auto& indices = *indices_array_pointer;
1599 switch (primitive.mode) {
1600 case TINYGLTF_MODE_TRIANGLES:
1601 for (
size_t i = 0; i < indices_accessor.count;
1604 indices[i], indices[i + 1],
1608 case TINYGLTF_MODE_TRIANGLE_STRIP:
1609 for (
size_t i = 2; i < indices_accessor.count;
1612 indices[i - 2], indices[i - 1],
1616 case TINYGLTF_MODE_TRIANGLE_FAN:
1617 for (
size_t i = 2; i < indices_accessor.count;
1620 indices[0], indices[i - 1],
1628 if (gltf_node.matrix.size() > 0) {
1629 std::vector<double> matrix = gltf_node.matrix;
1630 Eigen::Matrix4d transform =
1631 Eigen::Map<Eigen::Matrix4d>(&matrix[0], 4, 4);
1637 if (gltf_node.scale.size() > 0) {
1638 Eigen::Matrix4d transform = Eigen::Matrix4d::Identity();
1639 transform(0, 0) = gltf_node.scale[0];
1640 transform(1, 1) = gltf_node.scale[1];
1641 transform(2, 2) = gltf_node.scale[2];
1644 if (gltf_node.rotation.size() > 0) {
1645 Eigen::Matrix4d transform = Eigen::Matrix4d::Identity();
1649 transform.topLeftCorner<3, 3>() =
1650 Eigen::Quaterniond(gltf_node.rotation[3],
1651 gltf_node.rotation[0],
1652 gltf_node.rotation[1],
1653 gltf_node.rotation[2])
1654 .toRotationMatrix();
1657 if (gltf_node.translation.size() > 0) {
1659 gltf_node.translation[0], gltf_node.translation[1],
1660 gltf_node.translation[2]));
1674 bool write_vertex_normals ,
1675 bool write_vertex_colors ,
1676 bool write_triangle_uvs ,
1677 bool print_progress) {
1680 "This file format does not support writing textures and uv "
1681 "coordinates. Consider using .obj");
1683 tinygltf::Model model;
1684 model.asset.generator =
"cloudViewer";
1685 model.asset.version =
"2.0";
1686 model.defaultScene = 0;
1690 size_t num_of_triangles = mesh.
size();
1693 unsigned char* temp =
nullptr;
1695 tinygltf::BufferView indices_buffer_view_array;
1696 bool save_indices_as_uint32 = num_of_vertices > 65536;
1697 indices_buffer_view_array.name = save_indices_as_uint32
1698 ?
"buffer-0-bufferview-uint"
1699 :
"buffer-0-bufferview-ushort";
1700 indices_buffer_view_array.target = TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
1701 indices_buffer_view_array.buffer = 0;
1702 indices_buffer_view_array.byteLength = 0;
1703 model.bufferViews.push_back(indices_buffer_view_array);
1704 size_t indices_buffer_view_index = model.bufferViews.size() - 1;
1706 tinygltf::BufferView buffer_view_array;
1707 buffer_view_array.name =
"buffer-0-bufferview-vec3",
1708 buffer_view_array.target = TINYGLTF_TARGET_ARRAY_BUFFER;
1709 buffer_view_array.buffer = 0;
1710 buffer_view_array.byteLength = 0;
1711 buffer_view_array.byteOffset = 0;
1712 buffer_view_array.byteStride = 12;
1713 model.bufferViews.push_back(buffer_view_array);
1714 size_t mesh_attributes_buffer_view_index = model.bufferViews.size() - 1;
1716 tinygltf::Scene gltf_scene;
1717 gltf_scene.nodes.push_back(0);
1718 model.scenes.push_back(gltf_scene);
1720 tinygltf::Node gltf_node;
1722 model.nodes.push_back(gltf_node);
1724 tinygltf::Mesh gltf_mesh;
1725 tinygltf::Primitive gltf_primitive;
1727 tinygltf::Accessor indices_accessor;
1728 indices_accessor.name =
"buffer-0-accessor-indices-buffer-0-mesh-0";
1729 indices_accessor.type = TINYGLTF_TYPE_SCALAR;
1730 indices_accessor.componentType =
1731 save_indices_as_uint32 ? TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT
1732 : TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
1733 indices_accessor.count = 3 * num_of_triangles;
1735 3 * num_of_triangles *
1736 (save_indices_as_uint32 ?
sizeof(uint32_t) :
sizeof(uint16_t));
1738 indices_accessor.bufferView = int(indices_buffer_view_index);
1739 indices_accessor.byteOffset =
1740 model.bufferViews[indices_buffer_view_index].byteLength;
1741 model.bufferViews[indices_buffer_view_index].byteLength += byte_length;
1743 std::vector<unsigned char> index_buffer;
1744 for (
size_t tidx = 0; tidx < num_of_triangles; ++tidx) {
1748 save_indices_as_uint32 ?
sizeof(uint32_t) :
sizeof(uint16_t);
1749 for (
size_t i = 0; i < 3; ++i) {
1750 temp = (
unsigned char*)&(triangle(i));
1751 for (
size_t j = 0; j < uint_size; ++j) {
1752 index_buffer.push_back(temp[j]);
1757 indices_accessor.minValues.push_back(0);
1758 indices_accessor.maxValues.push_back(3 *
int(num_of_triangles) - 1);
1759 model.accessors.push_back(indices_accessor);
1760 gltf_primitive.indices = int(model.accessors.size()) - 1;
1762 tinygltf::Accessor positions_accessor;
1763 positions_accessor.name =
"buffer-0-accessor-position-buffer-0-mesh-0";
1764 positions_accessor.type = TINYGLTF_TYPE_VEC3;
1765 positions_accessor.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT;
1766 positions_accessor.count = num_of_vertices;
1767 byte_length = 3 * num_of_vertices *
sizeof(float);
1768 positions_accessor.bufferView = int(mesh_attributes_buffer_view_index);
1769 positions_accessor.byteOffset =
1770 model.bufferViews[mesh_attributes_buffer_view_index].byteLength;
1771 model.bufferViews[mesh_attributes_buffer_view_index].byteLength +=
1774 std::vector<unsigned char> mesh_attribute_buffer;
1775 for (
size_t vidx = 0; vidx < num_of_vertices; ++vidx) {
1776 Eigen::Vector3d vertex = mesh.
getVertice(vidx);
1777 for (
size_t i = 0; i < 3; ++i) {
1778 float_temp = (float)vertex(i);
1779 temp = (
unsigned char*)&(float_temp);
1780 for (
size_t j = 0; j <
sizeof(float); ++j) {
1781 mesh_attribute_buffer.push_back(temp[j]);
1787 positions_accessor.minValues.push_back(min_bound[0]);
1788 positions_accessor.minValues.push_back(min_bound[1]);
1789 positions_accessor.minValues.push_back(min_bound[2]);
1791 positions_accessor.maxValues.push_back(max_bound[0]);
1792 positions_accessor.maxValues.push_back(max_bound[1]);
1793 positions_accessor.maxValues.push_back(max_bound[2]);
1794 model.accessors.push_back(positions_accessor);
1796 "POSITION",
static_cast<int>(model.accessors.size()) - 1));
1798 write_vertex_normals = write_vertex_normals && mesh.
hasNormals();
1801 if (write_vertex_normals) {
1802 tinygltf::Accessor normals_accessor;
1803 normals_accessor.name =
"buffer-0-accessor-normal-buffer-0-mesh-0";
1804 normals_accessor.type = TINYGLTF_TYPE_VEC3;
1805 normals_accessor.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT;
1808 normals_accessor.bufferView = int(mesh_attributes_buffer_view_index);
1809 normals_accessor.byteOffset =
1810 model.bufferViews[mesh_attributes_buffer_view_index].byteLength;
1811 model.bufferViews[mesh_attributes_buffer_view_index].byteLength +=
1815 for (
size_t vidx = 0; vidx < num_of_vertices; ++vidx) {
1817 for (
size_t i = 0; i < 3; ++i) {
1818 float_temp = (float)
normal(i);
1819 temp = (
unsigned char*)&(float_temp);
1820 for (
size_t j = 0; j <
sizeof(float); ++j) {
1821 mesh_attribute_buffer.push_back(temp[j]);
1827 model.accessors.push_back(normals_accessor);
1829 "NORMAL",
static_cast<int>(model.accessors.size()) - 1));
1832 write_vertex_colors = write_vertex_colors && mesh.
hasColors();
1833 if (write_vertex_colors) {
1834 tinygltf::Accessor colors_accessor;
1835 colors_accessor.name =
"buffer-0-accessor-color-buffer-0-mesh-0";
1836 colors_accessor.type = TINYGLTF_TYPE_VEC3;
1837 colors_accessor.componentType = TINYGLTF_COMPONENT_TYPE_FLOAT;
1840 colors_accessor.bufferView = int(mesh_attributes_buffer_view_index);
1841 colors_accessor.byteOffset =
1842 model.bufferViews[mesh_attributes_buffer_view_index].byteLength;
1843 model.bufferViews[mesh_attributes_buffer_view_index].byteLength +=
1847 for (
size_t vidx = 0; vidx < num_of_vertices; ++vidx) {
1851 for (
size_t i = 0; i < 3; ++i) {
1852 float_temp = (float)
color(i);
1853 temp = (
unsigned char*)&(float_temp);
1854 for (
size_t j = 0; j <
sizeof(float); ++j) {
1855 mesh_attribute_buffer.push_back(temp[j]);
1861 model.accessors.push_back(colors_accessor);
1863 "COLOR_0",
static_cast<int>(model.accessors.size()) - 1));
1866 gltf_primitive.mode = TINYGLTF_MODE_TRIANGLES;
1867 gltf_mesh.primitives.push_back(gltf_primitive);
1868 model.meshes.push_back(gltf_mesh);
1870 model.bufferViews[0].byteOffset = 0;
1871 model.bufferViews[1].byteOffset = index_buffer.size();
1873 tinygltf::Buffer buffer;
1875 buffer.data.resize(index_buffer.size() + mesh_attribute_buffer.size());
1876 memcpy(buffer.data.data(), index_buffer.data(), index_buffer.size());
1877 memcpy(buffer.data.data() + index_buffer.size(),
1878 mesh_attribute_buffer.data(), mesh_attribute_buffer.size());
1879 model.buffers.push_back(buffer);
1881 tinygltf::TinyGLTF loader;
1882 std::string filename_ext =
1884 if (filename_ext ==
"glb") {
1885 if (!loader.WriteGltfSceneToFile(&model,
filename,
false,
true,
true,
1891 if (!loader.WriteGltfSceneToFile(&model,
filename,
false,
true,
true,
Vector3Tpl< PointCoordinateType > CCVector3
Default 3D Vector.
std::shared_ptr< core::Tensor > image
cmdLineReadable * params[]
boost::geometry::model::polygon< point_xy > polygon
Array of compressed 3D normals (single index)
virtual void showColors(bool state)
Sets colors visibility.
static ccPointCloud * ToPointCloud(ccHObject *obj, bool *isLockedVertices=nullptr)
Converts current object to 'equivalent' ccPointCloud.
virtual bool addChild(ccHObject *child, int dependencyFlags=DP_PARENT_OF_OTHER, int insertIndex=-1)
Adds a child.
NormsIndexesTableType * getTriNormsTable() const override
Returns per-triangle normals shared array.
cloudViewer::VerticesIndexes * getTriangleVertIndexes(unsigned triangleIndex) override
Returns the indexes of the vertices of a given triangle.
std::vector< int > triangle_material_ids_
List of material ids.
std::vector< Eigen::Vector2d > triangle_uvs_
List of uv coordinates per triangle.
Eigen::Vector3d getTriangleNorm(size_t index) const
bool HasVertexNormals() const
bool hasColors() const override
Returns whether colors are enabled or not.
bool reserve(std::size_t n)
Reserves the memory to store the vertex indexes (3 per triangle)
ccGenericPointCloud * getAssociatedCloud() const override
Returns the vertices cloud.
bool hasEigenTextures() const
Returns true if the mesh has texture.
Eigen::Vector3d getVertice(size_t index) const
void addTriangle(unsigned i1, unsigned i2, unsigned i3)
Adds a triangle to the mesh.
bool reservePerTriangleNormalIndexes()
Reserves memory to store per-triangle triplets of normal indexes.
unsigned int getVerticeSize() const
virtual void getTriangleVertices(unsigned triangleIndex, CCVector3 &A, CCVector3 &B, CCVector3 &C) const override
Returns the vertices of a given triangle.
Eigen::Vector3d getVertexColor(size_t index) const
bool hasNormals() const override
Returns whether normals are enabled or not.
virtual unsigned size() const override
Returns the number of triangles.
virtual Eigen::Vector3d GetMinBound() const override
Returns min bounds for geometry coordinates.
bool hasTriangleMaterialIds() const
std::vector< cloudViewer::geometry::Image > textures_
Textures of the image.
bool addTriangleNorm(const CCVector3 &N)
Eigen::Vector3d getVertexNormal(size_t index) const
void shrinkToFit()
Removes unused capacity.
std::vector< std::pair< std::string, Material > > materials_
virtual ccMesh & Transform(const Eigen::Matrix4d &transformation) override
Apply transformation (4x4 matrix) to the geometry coordinates.
bool hasTriangleUvs() const
virtual ccMesh & Translate(const Eigen::Vector3d &translation, bool relative=true) override
Apply translation to the geometry coordinates.
virtual Eigen::Vector3d GetMaxBound() const override
Returns max bounds for geometry coordinates.
bool hasTriNormals() const override
Returns whether the mesh has per-triangle normals.
virtual void setLocked(bool state)
Sets the "enabled" property.
virtual void setEnabled(bool state)
Sets the "enabled" property.
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
void addEigenNorm(const Eigen::Vector3d &N)
bool resizeTheNormsTable()
Resizes the compressed normals array.
void addEigenColor(const Eigen::Vector3d &color)
Eigen::Vector3d getEigenNormal(size_t index) const
bool resizeTheRGBTable(bool fillWithWhite=false)
Resizes the RGB colors array.
bool reserveTheRGBTable()
Reserves memory to store the RGB colors.
void unallocateNorms()
Erases the cloud normals.
bool resize(unsigned numberOfPoints) override
Resizes all the active features arrays.
void setPointColor(size_t pointIndex, const ecvColor::Rgb &col)
Sets a particular point color.
void setPointNormal(size_t pointIndex, const CCVector3 &N)
Sets a particular point normal (shortcut)
const ecvColor::Rgb & getPointColor(unsigned pointIndex) const override
Returns color corresponding to a given point.
void shrinkToFit()
Removes unused capacity.
void addRGBColor(const ecvColor::Rgb &C)
Pushes an RGB color on stack.
bool reserveThePointsTable(unsigned _numberOfPoints)
Reserves memory to store the points coordinates.
virtual bool hasTriangles() const
void addEigenPoint(const Eigen::Vector3d &point)
CCVector3 * getPointPtr(size_t index)
unsigned size() const override
void SetTotal(int64_t total)
constexpr static RgbTpl FromEigen(const Eigen::Vector3d &t)
static Eigen::Vector3d ToEigen(const Type col[3])
std::vector< unsigned int > face
utility::CountingProgressReporter * progress_bar
__host__ __device__ float3 cross(float3 a, float3 b)
__host__ __device__ float length(float2 v)
unsigned char ColorCompType
Default color components type (R,G and B)
Helper functions for the ml ops.
QTextStream & endl(QTextStream &stream)
CLOUDVIEWER_HOST_DEVICE Pair< First, Second > make_pair(const First &_first, const Second &_second)
bool ReadTriangleMeshFromOBJ(const std::string &filename, ccMesh &mesh, const ReadTriangleMeshOptions &)
bool AutoWriteMesh(const std::string &filename, const ccMesh &mesh, bool write_ascii=false, bool compressed=false, bool write_vertex_normals=true, bool write_vertex_colors=true, bool write_triangle_uvs=true, bool print_progress=false)
bool ReadTriangleMeshFromOFF(const std::string &filename, ccMesh &mesh, const ReadTriangleMeshOptions ¶ms)
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 WriteImage(const std::string &filename, const geometry::Image &image, int quality=kCloudViewerImageIODefaultQuality)
bool ReadTriangleMeshFromGLTF(const std::string &filename, ccMesh &mesh, const ReadTriangleMeshOptions ¶ms)
std::shared_ptr< ccMesh > CreateMeshFromFile(const std::string &filename, bool print_progress)
bool AddTrianglesByEarClipping(ccMesh &mesh, std::vector< unsigned int > &indices)
std::shared_ptr< geometry::Image > CreateImageFromFile(const std::string &filename)
bool ReadTriangleMeshUsingASSIMP(const std::string &filename, ccMesh &mesh, const ReadTriangleMeshOptions ¶ms)
bool WriteTriangleMeshToSTL(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)
FileGeometry ReadFileGeometryTypeOFF(const std::string &path)
FileGeometry ReadFileGeometryTypeOBJ(const std::string &path)
bool WriteTriangleMeshToPLY(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 AutoReadMesh(const std::string &filename, ccMesh &mesh, const ReadTriangleMeshOptions ¶ms={})
bool ReadTriangleMeshFromPLY(const std::string &filename, ccMesh &mesh, const ReadTriangleMeshOptions ¶ms)
FileGeometry ReadFileGeometryTypeGLTF(const std::string &path)
bool ReadTriangleMeshFromSTL(const std::string &filename, ccMesh &mesh, bool print_progress)
FileGeometry ReadFileGeometryTypeSTL(const std::string &path)
bool WriteTriangleMeshToGLTF(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 WriteTriangleMeshToOBJ(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)
FileGeometry ReadFileGeometryTypeFBX(const std::string &path)
bool IsPointInsidePolygon(const Eigen::MatrixX2d &polygon, double x, double y)
bool ReadTriangleMesh(const std::string &filename, ccMesh &mesh, ReadTriangleMeshOptions params)
bool WriteTriangleMeshToOFF(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)
static const std::string path
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
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
std::string GetFileParentDirectory(const std::string &filename)
std::string GetFileNameWithoutExtension(const std::string &filename)
std::string GetFileExtensionInLowerCase(const std::string &filename)
FILE * FOpen(const std::string &filename, const std::string &mode)
std::string GetFileNameWithoutDirectory(const std::string &filename)
std::string & StripString(std::string &str, const std::string &chars="\t\n\v\f\r ")
Eigen::Vector3uint8 ColorToUint8(const Eigen::Vector3d &color)
std::string ToUpper(const std::string &s)
Convert string to the upper case.
Generic file read and write utility for python interface.
Eigen::Matrix< Index, 3, 1 > Vector3i
std::string to_string(const T &n)
ArrayAdapter(const unsigned char *ptr, size_t count, size_t byte_stride)
T operator[](size_t pos) const
const unsigned char * data_ptr
virtual size_t size() const =0
virtual ~IntArrayBase()=default
virtual unsigned int operator[](size_t) const =0
IntArray(const ArrayAdapter< T > &a)
unsigned int operator[](size_t position) const override
size_t size() const override
ArrayAdapter< T > adapter