15 #include <tbb/parallel_for_each.h>
16 #include <vtkBooleanOperationPolyDataFilter.h>
17 #include <vtkCleanPolyData.h>
18 #include <vtkClipPolyData.h>
19 #include <vtkCutter.h>
20 #include <vtkFillHolesFilter.h>
22 #include <vtkQuadricDecimation.h>
37 #include <unordered_map>
38 #include <unordered_set>
85 "vertex_positions' device {} does not match "
86 "triangle_indices' device {}.",
97 size_t num_vertices = 0;
98 std::string vertex_dtype_str =
"";
99 size_t num_triangles = 0;
100 std::string triangles_dtype_str =
"";
113 "TriangleMesh on {} [{} vertices{} and {} triangles{}].",
115 num_triangles, triangles_dtype_str);
117 std::string vertices_attr_str =
"\nVertex Attributes:";
120 vertices_attr_str +=
" None.";
123 if (kv.first !=
"positions") {
125 fmt::format(
" {} (dtype = {}, shape = {}),", kv.first,
126 kv.second.GetDtype().ToString(),
127 kv.second.GetShape().ToString());
130 vertices_attr_str[vertices_attr_str.size() - 1] =
'.';
133 std::string triangles_attr_str =
"\nTriangle Attributes:";
136 triangles_attr_str +=
" None.";
139 if (kv.first !=
"indices") {
140 triangles_attr_str +=
141 fmt::format(
" {} (dtype = {}, shape = {}),", kv.first,
142 kv.second.GetDtype().ToString(),
143 kv.second.GetShape().ToString());
146 triangles_attr_str[triangles_attr_str.size() - 1] =
'.';
149 return str + vertices_attr_str + triangles_attr_str;
214 CUDA_CALL(kernel::trianglemesh::NormalizeNormalsCUDA,
229 CUDA_CALL(kernel::trianglemesh::NormalizeNormalsCUDA,
262 CUDA_CALL(kernel::trianglemesh::ComputeTriangleNormalsCUDA,
302 CUDA_CALL(kernel::trianglemesh::ComputeVertexNormalsCUDA,
324 }
else if (mesh.
IsCUDA()) {
325 CUDA_CALL(kernel::trianglemesh::ComputeTriangleAreasCUDA,
332 return triangle_areas;
350 "TriangleMesh already has triangle areas: remove "
351 "'areas' triangle attribute if you'd like to update.");
362 double surface_area = 0;
405 colors, float_dtype, device));
413 normals, float_dtype, device));
418 const size_t tri_count = mesh_legacy.
size();
420 std::vector<Eigen::Vector3i> tris;
421 tris.reserve(tri_count);
422 for (
size_t i = 0; i < tri_count; ++i) {
429 tris, int_dtype, device));
442 const auto &mat = mesh_legacy.
materials_.begin()->second;
445 tmat.SetBaseColor(Eigen::Vector4f{mat.baseColor.f4});
446 tmat.SetBaseRoughness(mat.baseRoughness);
447 tmat.SetBaseMetallic(mat.baseMetallic);
448 tmat.SetBaseReflectance(mat.baseReflectance);
449 tmat.SetAnisotropy(mat.baseAnisotropy);
450 tmat.SetBaseClearcoat(mat.baseClearCoat);
451 tmat.SetBaseClearcoatRoughness(mat.baseClearCoatRoughness);
460 if (mat.ambientOcclusion)
464 if (mat.clearCoatRoughness)
465 tmat.SetClearcoatRoughnessMap(
472 "Legacy mesh has more than 1 material which is not supported "
473 "by Tensor-based mesh. Only material {} was converted.",
481 mesh_legacy.CreateInternalCloud();
500 for (
const auto &t : tris) {
501 mesh_legacy.
addTriangle(
static_cast<unsigned>(t(0)),
502 static_cast<unsigned>(t(1)),
503 static_cast<unsigned>(t(2)));
513 "texture_uvs as a vertex attribute is not "
514 "supported by legacy TriangleMesh. Ignored.");
519 if (tmat.IsValid()) {
521 mesh_legacy.
materials_.front().first =
"Mat1";
522 auto &legacy_mat = mesh_legacy.
materials_.front().second;
524 if (tmat.HasBaseColor()) {
525 legacy_mat.baseColor.f4[0] = tmat.GetBaseColor().x();
526 legacy_mat.baseColor.f4[1] = tmat.GetBaseColor().y();
527 legacy_mat.baseColor.f4[2] = tmat.GetBaseColor().z();
528 legacy_mat.baseColor.f4[3] = tmat.GetBaseColor().w();
530 if (tmat.HasBaseRoughness()) {
531 legacy_mat.baseRoughness = tmat.GetBaseRoughness();
533 if (tmat.HasBaseMetallic()) {
534 legacy_mat.baseMetallic = tmat.GetBaseMetallic();
536 if (tmat.HasBaseReflectance()) {
537 legacy_mat.baseReflectance = tmat.GetBaseReflectance();
539 if (tmat.HasBaseClearcoat()) {
540 legacy_mat.baseClearCoat = tmat.GetBaseClearcoat();
542 if (tmat.HasBaseClearcoatRoughness()) {
543 legacy_mat.baseClearCoatRoughness =
544 tmat.GetBaseClearcoatRoughness();
546 if (tmat.HasAnisotropy()) {
547 legacy_mat.baseAnisotropy = tmat.GetAnisotropy();
550 if (tmat.HasAlbedoMap()) {
552 std::make_shared<cloudViewer::geometry::Image>();
553 *legacy_mat.albedo = tmat.GetAlbedoMap().ToLegacy();
555 if (tmat.HasNormalMap()) {
556 legacy_mat.normalMap =
557 std::make_shared<cloudViewer::geometry::Image>();
558 *legacy_mat.normalMap = tmat.GetNormalMap().ToLegacy();
560 if (tmat.HasAOMap()) {
561 legacy_mat.ambientOcclusion =
562 std::make_shared<cloudViewer::geometry::Image>();
563 *legacy_mat.ambientOcclusion = tmat.GetAOMap().ToLegacy();
565 if (tmat.HasMetallicMap()) {
566 legacy_mat.metallic =
567 std::make_shared<cloudViewer::geometry::Image>();
568 *legacy_mat.metallic = tmat.GetMetallicMap().ToLegacy();
570 if (tmat.HasRoughnessMap()) {
571 legacy_mat.roughness =
572 std::make_shared<cloudViewer::geometry::Image>();
573 *legacy_mat.roughness = tmat.GetRoughnessMap().ToLegacy();
575 if (tmat.HasReflectanceMap()) {
576 legacy_mat.reflectance =
577 std::make_shared<cloudViewer::geometry::Image>();
578 *legacy_mat.reflectance = tmat.GetReflectanceMap().ToLegacy();
580 if (tmat.HasClearcoatMap()) {
581 legacy_mat.clearCoat =
582 std::make_shared<cloudViewer::geometry::Image>();
583 *legacy_mat.clearCoat = tmat.GetClearcoatMap().ToLegacy();
585 if (tmat.HasClearcoatRoughnessMap()) {
586 legacy_mat.clearCoatRoughness =
587 std::make_shared<cloudViewer::geometry::Image>();
588 *legacy_mat.clearCoatRoughness =
589 tmat.GetClearcoatRoughnessMap().ToLegacy();
591 if (tmat.HasAnisotropyMap()) {
592 legacy_mat.anisotropy =
593 std::make_shared<cloudViewer::geometry::Image>();
594 *legacy_mat.anisotropy = tmat.GetAnisotropyMap().ToLegacy();
601 std::unordered_map<std::string, geometry::TriangleMesh>
607 std::unordered_map<std::string, TriangleMesh> tmeshes;
608 for (
const auto &mobj : model.
meshes_) {
616 tmeshes.emplace(mobj.mesh_name, tmesh);
642 using namespace vtkutils;
658 vtkNew<vtkPlane> clipPlane;
659 clipPlane->SetNormal(normal_.GetDataPtr<
double>());
660 clipPlane->SetOrigin(point_.GetDataPtr<
double>());
661 vtkNew<vtkClipPolyData> clipper;
662 clipper->SetInputData(polydata);
663 clipper->SetClipFunction(clipPlane);
664 vtkNew<vtkCleanPolyData> cleaner;
665 cleaner->SetInputConnection(clipper->GetOutputPort());
667 auto clipped_polydata = cleaner->GetOutput();
674 const std::vector<double> contour_values)
const {
675 using namespace vtkutils;
690 vtkNew<vtkPlane> clipPlane;
691 clipPlane->SetNormal(normal_.GetDataPtr<
double>());
692 clipPlane->SetOrigin(point_.GetDataPtr<
double>());
694 vtkNew<vtkCutter> cutter;
695 cutter->SetInputData(polydata);
696 cutter->SetCutFunction(clipPlane);
697 cutter->GenerateTrianglesOff();
698 cutter->SetNumberOfContours(contour_values.size());
700 for (
double value : contour_values) {
701 cutter->SetValue(i++, value);
704 auto slices_polydata = cutter->GetOutput();
710 double target_reduction,
bool preserve_volume)
const {
711 using namespace vtkutils;
712 if (target_reduction >= 1.0 || target_reduction < 0) {
714 "target_reduction must be in the range [0,1) but is {}",
721 vtkNew<vtkQuadricDecimation> decimate;
722 decimate->SetInputData(polydata);
723 decimate->SetTargetReduction(target_reduction);
724 decimate->SetVolumePreservation(preserve_volume);
726 auto decimated_polydata = decimate->GetOutput();
736 using namespace vtkutils;
744 vtkNew<vtkCleanPolyData> cleaner_A;
745 cleaner_A->SetInputData(polydata_A);
747 vtkNew<vtkCleanPolyData> cleaner_B;
748 cleaner_B->SetInputData(polydata_B);
750 vtkNew<vtkBooleanOperationPolyDataFilter> boolean_filter;
751 boolean_filter->SetOperation(op);
752 boolean_filter->SetTolerance(tolerance);
753 boolean_filter->SetInputConnection(0, cleaner_A->GetOutputPort());
754 boolean_filter->SetInputConnection(1, cleaner_B->GetOutputPort());
755 boolean_filter->Update();
756 auto out_polydata = boolean_filter->GetOutput();
763 double tolerance)
const {
764 return BooleanOperation(*
this, mesh, tolerance,
765 vtkBooleanOperationPolyDataFilter::VTK_UNION);
769 double tolerance)
const {
770 return BooleanOperation(
771 *
this, mesh, tolerance,
772 vtkBooleanOperationPolyDataFilter::VTK_INTERSECTION);
776 double tolerance)
const {
777 return BooleanOperation(*
this, mesh, tolerance,
778 vtkBooleanOperationPolyDataFilter::VTK_DIFFERENCE);
790 using namespace vtkutils;
795 vtkNew<vtkFillHolesFilter> fill_holes;
796 fill_holes->SetInputData(polydata);
797 fill_holes->SetHoleSize(hole_size);
798 fill_holes->Update();
799 auto result = fill_holes->GetOutput();
807 int parallel_partitions,
811 parallel_partitions, nthreads);
830 template <
class TAttr,
class TInt,
bool VERTEX_ATTR>
844 const int num_components =
845 components_shape.NumElements();
848 const float threshold = (margin /
size) * (margin /
size);
849 Eigen::Map<const Eigen::MatrixXf> sqrdistance_map(
851 Eigen::Map<Eigen::Matrix<TAttr, Eigen::Dynamic, Eigen::Dynamic>> tex_map(
853 Eigen::Map<const Eigen::Matrix<uint32_t, Eigen::Dynamic, Eigen::Dynamic>>
855 Eigen::Map<const Eigen::MatrixXf> uv_map(primitive_uvs.
GetDataPtr<
float>(),
857 Eigen::Map<const Eigen::Matrix<TAttr, Eigen::Dynamic, Eigen::Dynamic>>
858 attr_map(attr.
GetDataPtr<TAttr>(), num_components,
860 Eigen::Map<const Eigen::Matrix<TInt, 3, Eigen::Dynamic>>
861 triangle_indices_map(triangle_indices.
GetDataPtr<TInt>(), 3,
864 for (
int i = 0; i <
size; ++i) {
865 for (
int j = 0; j <
size; ++j) {
866 const int64_t linear_idx = i *
size + j;
867 if (sqrdistance_map(j, i) <= threshold) {
868 const uint32_t tid = tid_map(j, i);
870 const auto &a = attr_map.col(triangle_indices_map(0, tid));
871 const auto &b = attr_map.col(triangle_indices_map(1, tid));
872 const auto &c = attr_map.col(triangle_indices_map(2, tid));
873 TAttr u = uv_map(0, linear_idx);
874 TAttr v = uv_map(1, linear_idx);
875 tex_map.col(linear_idx) =
876 std::max<TAttr>(0, 1 - u - v) * a + u * b + v * c;
878 tex_map.col(linear_idx) = attr_map.col(tid);
881 tex_map.col(linear_idx).setConstant(fill_value);
899 void ComputePrimitiveInfoTexture(
int size,
900 core::Tensor &primitive_ids,
901 core::Tensor &primitive_uvs,
902 core::Tensor &sqrdistance,
903 const core::Tensor &texture_uvs) {
904 const int64_t num_triangles = texture_uvs.
GetLength();
907 core::Tensor vertices({num_triangles * 3, 3},
core::Float32);
909 const float *uv_ptr = texture_uvs.GetDataPtr<
float>();
910 float *v_ptr = vertices.GetDataPtr<
float>();
911 for (int64_t i = 0; i < texture_uvs.GetLength(); ++i) {
912 for (int64_t j = 0; j < 3; ++j) {
913 v_ptr[i * 9 + j * 3 + 0] = uv_ptr[i * 6 + j * 2 + 0];
914 v_ptr[i * 9 + j * 3 + 1] = uv_ptr[i * 6 + j * 2 + 1];
915 v_ptr[i * 9 + j * 3 + 2] = 0;
919 core::Tensor triangle_indices =
921 std::iota(triangle_indices.GetDataPtr<uint32_t>(),
922 triangle_indices.GetDataPtr<uint32_t>() +
923 triangle_indices.NumElements(),
926 RaycastingScene scene;
927 scene.AddTriangles(vertices, triangle_indices);
929 core::Tensor query_points =
931 float *ptr = query_points.
GetDataPtr<
float>();
932 for (
int i = 0; i <
size; ++i) {
933 float v = 1 - (i + 0.5f) /
size;
934 for (
int j = 0; j <
size; ++j) {
935 float u = (j + 0.5f) /
size;
936 ptr[i *
size * 3 + j * 3 + 0] = u;
937 ptr[i *
size * 3 + j * 3 + 1] = v;
938 ptr[i *
size * 3 + j * 3 + 2] = 0;
942 auto ans = scene.ComputeClosestPoints(query_points);
944 Eigen::Map<Eigen::MatrixXf> query_points_map(
945 query_points.GetDataPtr<
float>(), 3,
size *
size);
946 Eigen::Map<Eigen::MatrixXf> closest_points_map(
947 ans[
"points"].GetDataPtr<float>(), 3,
size *
size);
949 Eigen::Map<Eigen::VectorXf> sqrdistance_map(sqrdistance.GetDataPtr<
float>(),
952 (closest_points_map - query_points_map).colwise().squaredNorm();
953 primitive_ids = ans[
"primitive_ids"];
954 primitive_uvs = ans[
"primitive_uvs"];
956 void UpdateMaterialTextures(
957 std::unordered_map<std::string, core::Tensor> &textures,
958 visualization::rendering::Material &material) {
959 for (
auto &tex : textures) {
964 if (tex.second.
NumDims() > 2) {
965 shape.push_back(element_shape.NumElements());
968 core::Tensor img_data = tex.second.
Reshape(shape);
969 material.SetTextureMap(tex.first, Image(img_data));
974 std::unordered_map<std::string, core::Tensor>
977 const std::unordered_set<std::string> &vertex_attr,
980 bool update_material) {
981 if (!vertex_attr.size()) {
982 return std::unordered_map<std::string, core::Tensor>();
996 float *uv_ptr = texture_uvs.
GetDataPtr<
float>();
997 float *v_ptr = vertices.GetDataPtr<
float>();
998 for (int64_t i = 0; i < texture_uvs.
GetLength(); ++i) {
999 for (int64_t j = 0; j < 3; ++j) {
1000 v_ptr[i * 9 + j * 3 + 0] = uv_ptr[i * 6 + j * 2 + 0];
1001 v_ptr[i * 9 + j * 3 + 1] = uv_ptr[i * 6 + j * 2 + 1];
1002 v_ptr[i * 9 + j * 3 + 2] = 0;
1008 std::iota(triangle_indices.
GetDataPtr<uint32_t>(),
1013 core::Tensor primitive_ids, primitive_uvs, sqrdistance;
1014 ComputePrimitiveInfoTexture(
size, primitive_ids, primitive_uvs, sqrdistance,
1017 std::unordered_map<std::string, core::Tensor>
result;
1018 for (
auto attr : vertex_attr) {
1026 core::Tensor tex = BakeAttribute<scalar_t, int_t, true>(
1028 primitive_ids, primitive_uvs, sqrdistance,
1033 if (update_material) {
1040 std::unordered_map<std::string, core::Tensor>
1043 const std::unordered_set<std::string> &triangle_attr,
1046 bool update_material) {
1047 if (!triangle_attr.size()) {
1048 return std::unordered_map<std::string, core::Tensor>();
1059 core::Tensor primitive_ids, primitive_uvs, sqrdistance;
1060 ComputePrimitiveInfoTexture(
size, primitive_ids, primitive_uvs, sqrdistance,
1063 std::unordered_map<std::string, core::Tensor>
result;
1064 for (
auto attr : triangle_attr) {
1072 if (GetTriangleIndices().GetDtype() == core::Int32) {
1073 tex = BakeAttribute<scalar_t, int32_t, false>(
1074 size, margin, tensor, GetTriangleIndices(),
1075 primitive_ids, primitive_uvs, sqrdistance,
1078 tex = BakeAttribute<scalar_t, int64_t, false>(
1080 primitive_ids, primitive_uvs, sqrdistance,
1088 if (update_material) {
1099 bool capping)
const {
1100 using namespace vtkutils;
1102 translation, capping);
1107 bool capping)
const {
1108 using namespace vtkutils;
1120 int num_parititions;
1122 std::tie(num_parititions, partition_ids) =
1125 return num_parititions;
1131 template <
typename T>
1134 int64_t num_verts = vertex_mask.
GetLength();
1135 int64_t num_tris = tris_cpu.
GetLength();
1136 const T *vertex_mask_ptr = vertex_mask.
GetDataPtr<T>();
1137 std::vector<T> prefix_sum(num_verts + 1, 0);
1143 for (int64_t i = 0; i < num_tris * 3; ++i) {
1144 vert_idx_ptr[i] = prefix_sum[vert_idx_ptr[i]];
1161 item.second.IndexGet({vertex_mask}));
1170 item.second.IndexGet({tri_mask}));
1179 "[SelectFacesByMask] mesh has no vertex positions.");
1184 "[SelectFacesByMask] mesh has no triangle indices.");
1203 vertex_mask = core::Tensor::Zeros(
1204 {num_verts}, core::Dtype::FromType<scalar_tris_t>());
1205 const int64_t num_tris = tris_cpu.
GetLength();
1206 scalar_tris_t *vertex_mask_ptr =
1208 scalar_tris_t *vert_idx_ptr = tris_cpu.
GetDataPtr<scalar_tris_t>();
1210 for (int64_t i = 0; i < num_tris * 3; ++i) {
1211 vertex_mask_ptr[vert_idx_ptr[i]] = 1;
1213 UpdateTriangleIndicesByVertexMask<scalar_tris_t>(tris_cpu, vertex_mask);
1227 template <
typename T,
1228 typename std::enable_if<std::is_integral<T>::value &&
1229 !std::is_same<T, bool>::value &&
1230 std::is_signed<T>::value,
1238 template <
typename T,
1239 typename std::enable_if<std::is_integral<T>::value &&
1240 !std::is_same<T, bool>::value &&
1241 !std::is_signed<T>::value,
1248 bool copy_attributes )
const {
1265 "[SelectByIndex] indices are not of integral type {}.",
1289 indices_cpu.
GetDtype(), indices, [&]() {
1290 const int64_t num_tris = tris_cpu.GetLength();
1291 const int64_t num_verts = vertex_mask.GetLength();
1294 scalar_tris_t *vertex_mask_ptr =
1295 vertex_mask.GetDataPtr<scalar_tris_t>();
1296 const scalar_indices_t *indices_ptr =
1297 indices_cpu.GetDataPtr<scalar_indices_t>();
1298 for (int64_t i = 0; i < indices.GetLength(); ++i) {
1299 if (IsNegative(indices_ptr[i]) ||
1301 static_cast<scalar_indices_t>(num_verts)) {
1302 utility::LogWarning(
1303 "[SelectByIndex] indices contains index {} "
1309 vertex_mask_ptr[indices_ptr[i]] = 1;
1318 scalar_tris_t *tris_cpu_ptr =
1320 bool *tri_mask_ptr = tri_mask.
GetDataPtr<
bool>();
1321 for (int64_t i = 0; i < num_tris; ++i) {
1322 if (vertex_mask_ptr[tris_cpu_ptr[3 * i]] == 1 &&
1323 vertex_mask_ptr[tris_cpu_ptr[3 * i + 1]] == 1 &&
1324 vertex_mask_ptr[tris_cpu_ptr[3 * i + 2]] == 1) {
1325 tri_mask_ptr[i] =
true;
1329 tris_cpu = tris_cpu.
IndexGet({tri_mask});
1331 UpdateTriangleIndicesByVertexMask<scalar_tris_t>(
1332 tris_cpu, vertex_mask);
1339 if (tri_mask.NumElements() > 0) {
1344 result.SetVertexPositions(new_vertices);
1345 if (tris_cpu.NumElements() > 0) {
1348 if (copy_attributes)
1357 "[RemoveUnreferencedVertices] TriangleMesh has no vertices.");
1372 "[RemoveUnreferencedVertices] TriangleMesh has no triangles. "
1373 "Removing all vertices.");
1380 scalar_tris_t *tris_ptr = tris_cpu.
GetDataPtr<scalar_tris_t>();
1381 scalar_tris_t *vertex_mask_ptr =
1383 for (
int i = 0; i < tris_cpu.
GetLength(); i++) {
1384 vertex_mask_ptr[tris_ptr[3 * i]] = 1;
1385 vertex_mask_ptr[tris_ptr[3 * i + 1]] = 1;
1386 vertex_mask_ptr[tris_ptr[3 * i + 2]] = 1;
1389 UpdateTriangleIndicesByVertexMask<scalar_tris_t>(tris_cpu,
1399 SetVertexAttr(item.first, item.second.IndexGet({vertex_mask}));
1403 "[RemoveUnreferencedVertices] {:d} vertices have been removed.",
1419 xy_shape[t_xyz.
NumDims() - 1] = 2;
1425 Eigen::Map<Eigen::MatrixX<scalar_t>> xy(t_xy.
GetDataPtr<scalar_t>(), 2,
1427 Eigen::Map<const Eigen::MatrixX<scalar_t>> xyz(
1429 Eigen::Map<const Eigen::Matrix3<scalar_t>> KT(
1430 t_K.GetDataPtr<scalar_t>());
1431 Eigen::Map<const Eigen::Matrix4<scalar_t>> TT(
1432 t_T.GetDataPtr<scalar_t>());
1434 auto K = KT.transpose();
1435 auto T = TT.transpose();
1436 auto R = T.topLeftCorner<3, 3>();
1437 auto t = T.topRightCorner<3, 1>();
1438 auto pxyz = (K * ((R * xyz).colwise() + t)).array();
1439 xy = pxyz.topRows<2>().rowwise() / pxyz.bottomRows<1>();
1445 float get_min_cam_sqrdistance(
1446 const core::Tensor &positions,
1447 const std::vector<core::Tensor> &extrinsic_matrices) {
1448 const size_t MAXPTS = 10000;
1449 core::Tensor cam_loc({int64_t(extrinsic_matrices.size()), 3},
1451 for (
size_t k = 0; k < extrinsic_matrices.size(); ++k) {
1452 const core::Tensor RT = extrinsic_matrices[k].
Slice(0, 0, 3);
1454 -RT.
Slice(1, 0, 3).
T().
Matmul(RT.Slice(1, 3, 4)).Reshape({-1});
1456 size_t npts = positions.
GetShape(0);
1457 const core::Tensor pos_sample =
1458 npts > MAXPTS ? positions.Slice(0, 0, -1, npts / MAXPTS)
1460 auto nns = core::nns::NearestNeighborSearch(pos_sample);
1462 float min_sqrdistance = nns.KnnSearch(cam_loc, 1)
1466 return min_sqrdistance;
1472 const std::vector<Image> &images,
1473 const std::vector<core::Tensor> &intrinsic_matrices,
1474 const std::vector<core::Tensor> &extrinsic_matrices,
1476 bool update_material ) {
1479 "ProjectImagesToAlbedo is only supported on x86_64 CPU "
1484 constexpr
float EPS = 1e-6;
1487 "TriangleMesh does not contain 'texture_uvs'. Please compute "
1488 "it with ComputeUVAtlas() first.");
1495 if (images.size() != extrinsic_matrices.size() ||
1496 images.size() != intrinsic_matrices.size()) {
1498 "Received {} images, but {} extrinsic matrices and {} "
1499 "intrinsic matrices.",
1500 images.size(), extrinsic_matrices.size(),
1501 intrinsic_matrices.size());
1507 float min_sqr_distance =
1509 const float softmax_scale = 20 * min_sqr_distance;
1510 constexpr
float softmax_shift = 10.f, LOG_FLT_MAX = 88.f;
1515 tex_size, {
"positions"}, (tex_size + 255) / 256,
1517 false)[
"positions"];
1521 std::mutex albedo_mutex;
1527 size_t max_workers = tbb::this_task_arena::max_concurrency();
1529 std::vector<core::Tensor> this_albedo(max_workers,
1535 auto project_one_image = [&](
size_t i) {
1536 size_t widx = tbb::this_task_arena::current_thread_index();
1538 if (!this_albedo[widx].GetShape().IsCompatible(
1539 {tex_size, tex_size, 4})) {
1545 auto width = images[i].GetCols(),
height = images[i].GetRows();
1546 if (!weighted_image[widx].GetShape().IsCompatible({
height,
width, 4})) {
1547 weighted_image[widx] =
1556 intrinsic_matrices[i], extrinsic_matrices[i],
width,
height);
1562 auto result = tbb::this_task_arena::isolate(
1563 [&rays, &rcs]() {
return rcs.
CastRays(rays); });
1565 Eigen::Map<Eigen::ArrayXXf> normals_e(
1566 result[
"primitive_normals"].GetDataPtr<float>(), 3,
1568 Eigen::Map<Eigen::ArrayXXf> rays_e(rays.GetDataPtr<
float>(), 6,
1570 Eigen::Map<Eigen::ArrayXXf> t_hit(
result[
"t_hit"].GetDataPtr<float>(),
1572 auto depth = t_hit * rays_e.bottomRows<3>().colwise().norm().array();
1574 auto rays_dir = rays_e.bottomRows<3>().colwise().normalized().eval();
1575 auto pixel_foreshortening = (normals_e * rays_dir)
1580 auto inv_footprint =
1581 pixel_foreshortening.isNaN().select(0, pixel_foreshortening) /
1584 "[ProjectImagesToAlbedo] Image {}, weight (inv_footprint) "
1586 i, inv_footprint.minCoeff(), inv_footprint.maxCoeff());
1587 weighted_image[widx].Slice(2, 0, 3) =
1589 Eigen::Map<Eigen::MatrixXf> weighted_image_e(
1590 weighted_image[widx].GetDataPtr<float>(), 4,
width *
height);
1591 weighted_image_e.bottomRows<1>() = inv_footprint;
1595 uvrays[widx].GetItem({tk::Slice(0, None, 1), tk::Slice(0, None, 1),
1596 tk::Slice(0, 3, 1)}) = cam_loc;
1597 uvrays[widx].
GetItem({tk::Slice(0, None, 1), tk::Slice(0, None, 1),
1598 tk::Slice(3, 6, 1)}) = position_map - cam_loc;
1601 result = tbb::this_task_arena::isolate(
1602 [&rcs, &uvrays, widx]() {
return rcs.
CastRays(uvrays[widx]); });
1603 auto &t_hit_uv =
result[
"t_hit"];
1605 Project(position_map, intrinsic_matrices[i], extrinsic_matrices[i],
1608 for (
float *p_uv2xy = uv2xy[widx].GetDataPtr<float>(),
1609 *p_t_hit = t_hit_uv.GetDataPtr<
float>();
1611 uv2xy[widx].GetDataPtr<
float>() + uv2xy[widx].NumElements();
1612 p_uv2xy += 2, ++p_t_hit) {
1613 if (*p_t_hit < 1 -
EPS) *p_uv2xy = *(p_uv2xy + 1) = -1.f;
1616 uv2xy[widx].
Permute({2, 0, 1}).Contiguous();
1620 this_albedo[widx].
Fill(0.f);
1628 std::unique_lock<std::mutex> albedo_lock{albedo_mutex};
1630 for (
auto p_albedo = albedo.
GetDataPtr<
float>(),
1631 p_this_albedo = this_albedo[widx].GetDataPtr<
float>();
1633 p_albedo += 4, p_this_albedo += 4) {
1634 float softmax_weight =
1635 exp(
std::min(LOG_FLT_MAX, softmax_scale * p_this_albedo[3] -
1637 for (
auto k = 0; k < 3; ++k)
1638 p_albedo[k] += p_this_albedo[k] * softmax_weight;
1639 p_albedo[3] += softmax_weight;
1643 std::vector<size_t> range(images.size(), 0);
1644 std::iota(range.begin(), range.end(), 0);
1645 tbb::parallel_for_each(range, project_one_image);
1646 albedo.
Slice(2, 0, 3) /= albedo.
Slice(2, 3, 4);
1649 Image albedo_texture =
1652 if (update_material) {
1659 return albedo_texture;
1663 template <
typename T,
1664 typename std::enable_if<std::is_integral<T>::value &&
1665 !std::is_same<T, bool>::value,
1667 using Edge = std::tuple<T, T>;
1671 template <
typename T>
1673 return (vidx0 < vidx1) ?
Edge<T>{vidx0, vidx1} :
Edge<T>{vidx1, vidx0};
1676 template <
typename T>
1677 static std::unordered_map<Edge<T>,
1678 std::vector<size_t>,
1681 std::unordered_map<Edge<T>, std::vector<size_t>,
1684 auto AddEdge = [&](T vidx0, T vidx1, int64_t tidx) {
1687 const T *tris_ptr = tris_cpu.
GetDataPtr<T>();
1688 for (int64_t tidx = 0; tidx < tris_cpu.
GetLength(); ++tidx) {
1689 const T *triangle = &tris_ptr[3 * tidx];
1690 AddEdge(triangle[0], triangle[1], tidx);
1691 AddEdge(triangle[1], triangle[2], tidx);
1692 AddEdge(triangle[2], triangle[0], tidx);
1694 return tris_per_edge;
1700 "[RemoveNonManifildEdges] TriangleMesh has no vertices.");
1706 "[RemoveNonManifoldEdges] TriangleMesh has no triangles.");
1724 scalar_t *tri_areas_ptr = tri_areas_cpu.GetDataPtr<scalar_t>();
1725 auto edges_to_tris = GetEdgeToTrianglesMap<int_t>(tris_cpu);
1728 auto area_greater_compare = [&tri_areas_ptr](size_t lhs,
1730 return tri_areas_ptr[lhs] > tri_areas_ptr[rhs];
1736 for (
auto &kv : edges_to_tris) {
1740 auto tris_end = std::remove_if(
1741 kv.second.begin(), kv.second.end(),
1742 [=](
size_t t) { return tri_areas_ptr[t] < 0; });
1744 int n_tris = std::distance(kv.second.begin(), tris_end);
1756 kv.second.erase(tris_end, kv.second.end());
1759 std::nth_element(kv.second.begin(), kv.second.begin() + 1,
1760 kv.second.end(), area_greater_compare);
1763 for (
auto it = kv.second.begin() + 2; it < kv.second.end();
1765 tri_areas_ptr[*it] = -1;
1782 bool allow_boundary_edges )
const {
1785 "[GetNonManifoldEdges] TriangleMesh has no vertices.");
1791 "[GetNonManifoldEdges] TriangleMesh has no triangles.");
1801 auto edges = GetEdgeToTrianglesMap<scalar_tris_t>(tris_cpu);
1802 std::vector<scalar_tris_t> non_manifold_edges;
1804 for (
auto &kv : edges) {
1805 if ((allow_boundary_edges &&
1806 (kv.second.size() < 1 || kv.second.size() > 2)) ||
1807 (!allow_boundary_edges && kv.second.size() != 2)) {
1808 non_manifold_edges.push_back(std::get<0>(kv.first));
1809 non_manifold_edges.push_back(std::get<1>(kv.first));
1814 {(
long int)non_manifold_edges.size() / 2, 2},
1822 size_t number_of_points,
bool use_triangle_normal ) {
1823 if (number_of_points <= 0) {
1840 "SamplePointsUniformly is implemented only on CPU. Computing "
1852 auto float_dt = vertices.
GetDtype();
1877 if (use_vert_colors) {
1880 vertex_colors /= 65535;
1883 std::array<core::Tensor, 3>
result =
1885 triangles, vertices, areas, vertex_normals, vertex_colors,
1886 triangle_normals, texture_uvs, albedo, number_of_points);
1895 std::vector<Metric> metrics,
1902 "ComputeDistance is implemented only on CPU. Computing on "
1908 cpu_mesh1.SamplePointsUniformly(
params.n_sampled_points)
1909 .GetPointPositions();
1911 cpu_mesh2.SamplePointsUniformly(
params.n_sampled_points)
1912 .GetPointPositions();
#define CUDA_CALL(cuda_function,...)
#define DISPATCH_DTYPE_TO_TEMPLATE_WITH_BOOL(DTYPE,...)
#define DISPATCH_FLOAT_DTYPE_TO_TEMPLATE(DTYPE,...)
#define DISPATCH_FLOAT_INT_DTYPE_TO_TEMPLATE(FDTYPE, IDTYPE,...)
#define DISPATCH_INT_DTYPE_PREFIX_TO_TEMPLATE(DTYPE, PREFIX,...)
filament::Texture::InternalFormat format
#define IPP_CALL(ipp_function,...)
cmdLineReadable * params[]
#define AssertTensorDevice(tensor,...)
#define AssertTensorDtype(tensor,...)
#define AssertTensorDtypes(tensor,...)
#define AssertTensorShape(tensor,...)
void addEigenVertices(const std::vector< Eigen::Vector3d > &vertices)
cloudViewer::VerticesIndexes * getTriangleVertIndexes(unsigned triangleIndex) override
Returns the indexes of the vertices of a given triangle.
std::vector< Eigen::Vector2d > triangle_uvs_
List of uv coordinates per triangle.
std::vector< Eigen::Vector3d > getVertexColors() const
void addVertexColors(const std::vector< Eigen::Vector3d > &colors)
std::vector< Eigen::Vector3d > getEigenVertices() const
void addTriangle(unsigned i1, unsigned i2, unsigned i3)
Adds a triangle to the mesh.
std::vector< Eigen::Vector3d > getVertexNormals() const
virtual unsigned size() const override
Returns the number of triangles.
void addVertexNormals(const std::vector< Eigen::Vector3d > &normals)
std::vector< std::pair< std::string, Material > > materials_
bool hasTriangleUvs() const
std::string ToString() const
Returns string representation of device, e.g. "CPU:0", "CUDA:0".
std::string ToString() const
DtypeCode GetDtypeCode() const
iterator insert(iterator I, T &&Elt)
TensorKey is used to represent single index, slice or advanced indexing on a Tensor.
Tensor Contiguous() const
Tensor Matmul(const Tensor &rhs) const
Tensor Sum(const SizeVector &dims, bool keepdim=false) const
Tensor Gt(const Tensor &value) const
Element-wise greater-than of tensors, returning a new boolean tensor.
int64_t GetLength() const
Tensor GetItem(const TensorKey &tk) const
Tensor Sub_(const Tensor &value)
Tensor Permute(const SizeVector &dims) const
Permute (dimension shuffle) the Tensor, returns a view.
Tensor IndexGet(const std::vector< Tensor > &index_tensors) const
Advanced indexing getter. This will always allocate a new Tensor.
int64_t NumElements() const
Tensor Mul_(const Tensor &value)
static Tensor Zeros(const SizeVector &shape, Dtype dtype, const Device &device=Device("CPU:0"))
Create a tensor fill with zeros.
Tensor Add_(const Tensor &value)
Device GetDevice() const override
Tensor Reshape(const SizeVector &dst_shape) const
static Tensor Empty(const SizeVector &shape, Dtype dtype, const Device &device=Device("CPU:0"))
Create a tensor with uninitialized values.
SizeVector GetShape() const
void Fill(S v)
Fill the whole Tensor with a scalar value, the scalar will be casted to the Tensor's Dtype.
Tensor T() const
Expects input to be <= 2-D Tensor by swapping dimension 0 and 1.
Tensor Slice(int64_t dim, int64_t start, int64_t stop, int64_t step=1) const
Tensor To(Dtype dtype, bool copy=false) const
const SizeVector & GetShapeRef() const
A bounding box that is aligned along the coordinate axes and defined by the min_bound and max_bound.
static AxisAlignedBoundingBox CreateFromPoints(const core::Tensor &points)
bool HasMaterial() const
Check if a material has been applied to this Geometry with SetMaterial.
void SetMaterial(const visualization::rendering::Material &material)
Set the material properties associate with this Geometry.
visualization::rendering::Material & GetMaterial()
Get material associated with this Geometry.
GeometryType
Specifies possible geometry types.
The Image class stores image with customizable rows, cols, channels, dtype and device.
@ Linear
Bilinear interpolation.
static constexpr bool HAVE_IPP
Do we use IPP for accelerating image processing operations?
static Image FromLegacy(const cloudViewer::geometry::Image &image_legacy, const core::Device &Device=core::Device("CPU:0"))
Create from a legacy CloudViewer Image.
Image To(const core::Device &device, bool copy=false) const
Transfer the image to a specified device.
core::Tensor AsTensor() const
Returns the underlying Tensor of the Image.
A LineSet contains points and lines joining them and optionally attributes on the points and lines.
A bounding box oriented along an arbitrary frame of reference.
static OrientedBoundingBox CreateFromPoints(const core::Tensor &points, bool robust=false, MethodOBBCreate method=MethodOBBCreate::MINIMAL_APPROX)
A point cloud contains a list of 3D points.
void SetPointNormals(const core::Tensor &value)
Set the value of the "normals" attribute. Convenience function.
void SetPointColors(const core::Tensor &value)
Set the value of the "colors" attribute. Convenience function.
TriangleMesh ComputeConvexHull(bool joggle_inputs=false) const
PointCloud To(const core::Device &device, bool copy=false) const
A scene class with basic ray casting and closest point queries.
static core::Tensor CreateRaysPinhole(const core::Tensor &intrinsic_matrix, const core::Tensor &extrinsic_matrix, int width_px, int height_px)
Creates rays for the given camera parameters.
uint32_t AddTriangles(const core::Tensor &vertex_positions, const core::Tensor &triangle_indices)
Add a triangle mesh to the scene.
std::unordered_map< std::string, core::Tensor > CastRays(const core::Tensor &rays, const int nthreads=0) const
Computes the first intersection of the rays with the scene.
core::Tensor ComputeDistance(const core::Tensor &query_points, const int nthreads=0)
Computes the distance to the surface of the scene.
void AssertSizeSynchronized() const
Assert IsSizeSynchronized().
std::string GetPrimaryKey() const
Returns the primary key of the TensorMap.
TensorMap Contiguous() const
std::unordered_set< std::string > GetKeySet() const
Returns a set with all keys.
bool Contains(const std::string &key) const
A triangle mesh contains vertices and triangles.
bool HasTriangleNormals() const
TriangleMesh SimplifyQuadricDecimation(double target_reduction, bool preserve_volume=true) const
TriangleMesh(const core::Device &device=core::Device("CPU:0"))
TriangleMesh & Translate(const core::Tensor &translation, bool relative=true)
Translates the VertexPositions of the TriangleMesh.
core::Tensor ComputeMetrics(const TriangleMesh &mesh2, std::vector< Metric > metrics={Metric::ChamferDistance}, MetricParameters params=MetricParameters()) const
void SetTriangleNormals(const core::Tensor &value)
core::Tensor & GetTriangleIndices()
core::Tensor & GetVertexNormals()
TriangleMesh BooleanIntersection(const TriangleMesh &mesh, double tolerance=1e-6) const
double GetSurfaceArea() const
Function that computes the surface area of the mesh, i.e. the sum of the individual triangle surfaces...
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"))
TriangleMesh SelectFacesByMask(const core::Tensor &mask) const
const TensorMap & GetVertexAttr() const
Getter for vertex_attr_ TensorMap. Used in Pybind.
bool HasVertexNormals() const
Image ProjectImagesToAlbedo(const std::vector< Image > &images, const std::vector< core::Tensor > &intrinsic_matrices, const std::vector< core::Tensor > &extrinsic_matrices, int tex_size=1024, bool update_material=true)
static std::unordered_map< std::string, geometry::TriangleMesh > FromTriangleMeshModel(const cloudViewer::visualization::rendering::TriangleMeshModel &model, core::Dtype float_dtype=core::Float32, core::Dtype int_dtype=core::Int64, const core::Device &device=core::Device("CPU:0"))
TriangleMesh RemoveUnreferencedVertices()
std::unordered_map< std::string, core::Tensor > BakeTriangleAttrTextures(int size, const std::unordered_set< std::string > &triangle_attr={}, double margin=2., double fill=0., bool update_material=true)
bool HasVertexPositions() const
core::Tensor & GetVertexPositions()
std::string ToString() const
Text description.
TriangleMesh ClipPlane(const core::Tensor &point, const core::Tensor &normal) const
Clip mesh with a plane. This method clips the triangle mesh with the specified plane....
TriangleMesh RemoveNonManifoldEdges()
core::Tensor & GetTriangleNormals()
TriangleMesh ExtrudeRotation(double angle, const core::Tensor &axis, int resolution=16, double translation=0.0, bool capping=true) const
AxisAlignedBoundingBox GetAxisAlignedBoundingBox() const
Create an axis-aligned bounding box from vertex attribute "positions".
core::Tensor & GetVertexColors()
core::Device GetDevice() const override
Returns the device of the geometry.
std::unordered_map< std::string, core::Tensor > BakeVertexAttrTextures(int size, const std::unordered_set< std::string > &vertex_attr={}, double margin=2., double fill=0., bool update_material=true)
void SetVertexPositions(const core::Tensor &value)
bool HasTriangleIndices() const
TriangleMesh To(const core::Device &device, bool copy=false) const
core::Tensor GetNonManifoldEdges(bool allow_boundary_edges=true) const
TriangleMesh & NormalizeNormals()
Normalize both triangle normals and vertex normals to length 1.
void SetTriangleIndices(const core::Tensor &value)
Set the value of the "indices" attribute in triangle_attr_.
TriangleMesh BooleanUnion(const TriangleMesh &mesh, double tolerance=1e-6) const
PointCloud SamplePointsUniformly(size_t number_of_points, bool use_triangle_normal=false)
int PCAPartition(int max_faces)
ccMesh ToLegacy() const
Convert to a legacy CloudViewer TriangleMesh.
void SetVertexNormals(const core::Tensor &value)
TriangleMesh & Scale(double scale, const core::Tensor ¢er)
Scales the VertexPositions of the TriangleMesh.
TriangleMesh FillHoles(double hole_size=1e6) const
bool HasVertexAttr(const std::string &key) const
TriangleMesh & Rotate(const core::Tensor &R, const core::Tensor ¢er)
Rotates the VertexPositions, VertexNormals and TriangleNormals (if exists).
TriangleMesh & ComputeTriangleAreas()
Function to compute triangle areas and save it as a triangle attribute "areas". Prints a warning,...
TriangleMesh ExtrudeLinear(const core::Tensor &vector, double scale=1.0, bool capping=true) const
TriangleMesh BooleanDifference(const TriangleMesh &mesh, double tolerance=1e-6) const
bool HasVertexColors() const
std::tuple< float, int, int > ComputeUVAtlas(size_t size=512, float gutter=1.0f, float max_stretch=1.f/6, int parallel_partitions=1, int nthreads=0)
bool HasTriangleAttr(const std::string &key) const
LineSet SlicePlane(const core::Tensor &point, const core::Tensor &normal, const std::vector< double > contour_values={0.0}) const
Extract contour slices given a plane. This method extracts slices as LineSet from the mesh at specifi...
TriangleMesh ComputeConvexHull(bool joggle_inputs=false) const
TriangleMesh & ComputeVertexNormals(bool normalized=true)
Function to compute vertex normals, usually called before rendering.
void SetVertexAttr(const std::string &key, const core::Tensor &value)
void SetVertexColors(const core::Tensor &value)
TriangleMesh SelectByIndex(const core::Tensor &indices, bool copy_attributes=true) const
bool IsEmpty() const override
Returns !HasVertexPositions(), triangles are ignored.
void SetTriangleAttr(const std::string &key, const core::Tensor &value)
TriangleMesh & Transform(const core::Tensor &transformation)
Transforms the VertexPositions, VertexNormals and TriangleNormals (if exist) of the TriangleMesh.
const TensorMap & GetTriangleAttr() const
Getter for triangle_attr_ TensorMap. Used in Pybind.
TriangleMesh & ComputeTriangleNormals(bool normalized=true)
Function to compute triangle normals, usually called before rendering.
OrientedBoundingBox GetOrientedBoundingBox() const
Create an oriented bounding box from vertex attribute "positions".
core::Tensor GetCenter() const
void SetDefaultProperties()
bool HasAlbedoMap() const
static Material FromMaterialRecord(const MaterialRecord &mat)
Convert from MaterialRecord.
void SetAlbedoMap(const t::geometry::Image &image)
const t::geometry::Image & GetAlbedoMap() const
Helper functions for the ml ops.
std::vector< Eigen::Vector2d > TensorToEigenVector2dVector(const core::Tensor &tensor)
Converts a tensor of shape (N, 2) to std::vector<Eigen::Vector2d>. An exception will be thrown if the...
std::vector< Eigen::Vector3d > TensorToEigenVector3dVector(const core::Tensor &tensor)
Converts a tensor of shape (N, 3) to std::vector<Eigen::Vector3d>. An exception will be thrown if the...
core::Tensor EigenVector3dVectorToTensor(const std::vector< Eigen::Vector3d > &values, core::Dtype dtype, const core::Device &device)
Converts a vector of Eigen::Vector3d to a (N, 3) tensor. This function also takes care of dtype conve...
std::vector< Eigen::Vector3i > TensorToEigenVector3iVector(const core::Tensor &tensor)
Converts a tensor of shape (N, 3) to std::vector<Eigen::Vector3i>. An exception will be thrown if the...
core::Tensor EigenVector2dVectorToTensor(const std::vector< Eigen::Vector2d > &values, core::Dtype dtype, const core::Device &device)
Converts a vector of Eigen::Vector2d to a (N, 2) tensor. This function also takes care of dtype conve...
core::Tensor EigenVector3iVectorToTensor(const std::vector< Eigen::Vector3i > &values, core::Dtype dtype, const core::Device &device)
Converts a vector of Eigen::Vector3i to a (N, 3) tensor. This function also takes care of dtype conve...
constexpr utility::nullopt_t None
::ccPointCloud PointCloud
void Remap(const core::Tensor &src_im, const core::Tensor &dst2src_xmap, const core::Tensor &dst2src_ymap, core::Tensor &dst_im, Image::InterpType interp_type)
std::tuple< int, core::Tensor > PCAPartition(core::Tensor &points, int max_points)
void Project(core::Tensor &depth, utility::optional< std::reference_wrapper< core::Tensor >> image_colors, const core::Tensor &points, utility::optional< std::reference_wrapper< const core::Tensor >> colors, const core::Tensor &intrinsics, const core::Tensor &extrinsics, float depth_scale, float depth_max)
void ComputeTriangleNormalsCPU(const core::Tensor &vertices, const core::Tensor &triangles, core::Tensor &normals)
void ComputeVertexNormalsCPU(const core::Tensor &triangles, const core::Tensor &triangle_normals, core::Tensor &vertex_normals)
void NormalizeNormalsCPU(core::Tensor &normals)
void ComputeTriangleAreasCPU(const core::Tensor &vertices, const core::Tensor &triangles, core::Tensor &triangle_areas)
std::array< core::Tensor, 3 > SamplePointsUniformlyCPU(const core::Tensor &triangles, const core::Tensor &vertices, const core::Tensor &triangle_areas, const core::Tensor &vertex_normals, const core::Tensor &vertex_colors, const core::Tensor &triangle_normals, const core::Tensor &texture_uvs, const core::Tensor &albedo, size_t number_of_points)
std::tuple< float, int, int > ComputeUVAtlas(TriangleMesh &mesh, const size_t width, const size_t height, const float gutter, const float max_stretch, int parallel_partitions, int nthreads)
vtkSmartPointer< vtkPolyData > CreateVtkPolyDataFromGeometry(const Geometry &geometry, const std::unordered_set< std::string > &point_attr_include, const std::unordered_set< std::string > &face_attr_include, const std::unordered_set< std::string > &point_attr_exclude, const std::unordered_set< std::string > &face_attr_exclude, bool copy)
TriangleMesh CreateTriangleMeshFromVtkPolyData(vtkPolyData *polydata, bool copy)
CLOUDVIEWER_LOCAL TriangleMesh ExtrudeLinearTriangleMesh(const Geometry &geometry, const core::Tensor &vector, double scale, bool capping)
CLOUDVIEWER_LOCAL TriangleMesh ExtrudeRotationTriangleMesh(const Geometry &geometry, const double angle, const core::Tensor &axis, int resolution, double translation, bool capping)
CLOUDVIEWER_LOCAL LineSet CreateLineSetFromVtkPolyData(vtkPolyData *polydata, bool copy)
static void UpdateTriangleIndicesByVertexMask(core::Tensor &tris_cpu, const core::Tensor &vertex_mask)
static bool IsNegative(T val)
brief Static negative checker for signed integer types
static void CopyAttributesByMasks(TriangleMesh &dst, const TriangleMesh &src, const core::Tensor &vertex_mask, const core::Tensor &tri_mask)
static Edge< T > GetOrderedEdge(T vidx0, T vidx1)
brief Helper function to get an edge with ordered vertex indices.
static std::unordered_map< Edge< T >, std::vector< size_t >, utility::hash_tuple< Edge< T > > > GetEdgeToTrianglesMap(const core::Tensor &tris_cpu)
core::Tensor ComputeMetricsCommon(core::Tensor distance12, core::Tensor distance21, std::vector< Metric > metrics, MetricParameters params)
static core::Tensor ComputeTriangleAreasHelper(const TriangleMesh &mesh)
void InclusivePrefixSum(const Tin *first, const Tin *last, Tout *out)
Generic file read and write utility for python interface.
Eigen::Matrix< Index, 3, 1 > Vector3i
Holder for various parameters required by metrics.
std::vector< MeshInfo > meshes_
std::vector< visualization::rendering::MaterialRecord > materials_