diff --git a/CHANGELOG.md b/CHANGELOG.md index 96bedc8af1f..8965478f135 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ * Fix raycasting scene: Allow setting of number of threads that are used for building a raycasting scene * Fix Python bindings for CUDA device synchronization, voxel grid saving (PR #5425) * Support msgpack versions without cmake +* Changed TriangleMesh to store materials in a list so they can be accessed by the material index (PR #5938) * Support multi-threading in the RayCastingScene function to commit scene (PR #6051). * Fix some bad triangle generation in TriangleMesh::SimplifyQuadricDecimation diff --git a/cpp/open3d/geometry/TriangleMesh.h b/cpp/open3d/geometry/TriangleMesh.h index 505c255ac26..8448cccbcc0 100644 --- a/cpp/open3d/geometry/TriangleMesh.h +++ b/cpp/open3d/geometry/TriangleMesh.h @@ -853,7 +853,7 @@ class TriangleMesh : public MeshBase { std::unordered_map additionalMaps; }; - std::unordered_map materials_; + std::vector> materials_; /// List of material ids. std::vector triangle_material_ids_; diff --git a/cpp/open3d/io/file_format/FileASSIMP.cpp b/cpp/open3d/io/file_format/FileASSIMP.cpp index dec40259566..8604124dbed 100644 --- a/cpp/open3d/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/io/file_format/FileASSIMP.cpp @@ -237,12 +237,13 @@ bool ReadTriangleMeshUsingASSIMP( } // Now load the materials + mesh.materials_.resize(scene->mNumMaterials); for (size_t i = 0; i < scene->mNumMaterials; ++i) { auto* mat = scene->mMaterials[i]; - // create material structure to match this name - auto& mesh_material = - mesh.materials_[std::string(mat->GetName().C_Str())]; + // Set the material structure to match this name + auto& mesh_material = mesh.materials_[i].second; + mesh.materials_[i].first = mat->GetName().C_Str(); using MaterialParameter = geometry::TriangleMesh::Material::MaterialParameter; @@ -277,9 +278,9 @@ bool ReadTriangleMeshUsingASSIMP( // For legacy visualization support if (mesh_material.albedo) { - mesh.textures_.push_back(*mesh_material.albedo->FlipVertical()); + mesh.textures_.emplace_back(*mesh_material.albedo->FlipVertical()); } else { - mesh.textures_.push_back(geometry::Image()); + mesh.textures_.emplace_back(); } } diff --git a/cpp/open3d/io/file_format/FileOBJ.cpp b/cpp/open3d/io/file_format/FileOBJ.cpp index 3123d8ad05a..a06b30a97fb 100644 --- a/cpp/open3d/io/file_format/FileOBJ.cpp +++ b/cpp/open3d/io/file_format/FileOBJ.cpp @@ -140,8 +140,11 @@ bool ReadTriangleMeshFromOBJ(const std::string& filename, using MaterialParameter = geometry::TriangleMesh::Material::MaterialParameter; - for (auto& material : materials) { - auto& meshMaterial = mesh.materials_[material.name]; + mesh.materials_.resize(materials.size()); + for (std::size_t i = 0; i < materials.size(); ++i) { + auto& material = materials[i]; + mesh.materials_[i].first = material.name; + auto& meshMaterial = mesh.materials_[i].second; meshMaterial.baseColor = MaterialParameter::CreateRGB( material.diffuse[0], material.diffuse[1], material.diffuse[2]); diff --git a/cpp/open3d/t/geometry/TriangleMesh.cpp b/cpp/open3d/t/geometry/TriangleMesh.cpp index 4134ee6f306..a87abf04eb5 100644 --- a/cpp/open3d/t/geometry/TriangleMesh.cpp +++ b/cpp/open3d/t/geometry/TriangleMesh.cpp @@ -409,7 +409,9 @@ open3d::geometry::TriangleMesh TriangleMesh::ToLegacy() const { // Convert material if the t geometry has a valid one auto &tmat = GetMaterial(); if (tmat.IsValid()) { - auto &legacy_mat = mesh_legacy.materials_["Mat1"]; + mesh_legacy.materials_.emplace_back(); + mesh_legacy.materials_.front().first = "Mat1"; + auto &legacy_mat = mesh_legacy.materials_.front().second; // Convert scalar properties if (tmat.HasBaseColor()) { legacy_mat.baseColor.f4[0] = tmat.GetBaseColor().x(); diff --git a/cpp/open3d/visualization/visualizer/O3DVisualizer.cpp b/cpp/open3d/visualization/visualizer/O3DVisualizer.cpp index 5dc25714392..55e85b2d578 100644 --- a/cpp/open3d/visualization/visualizer/O3DVisualizer.cpp +++ b/cpp/open3d/visualization/visualizer/O3DVisualizer.cpp @@ -941,11 +941,24 @@ struct O3DVisualizer::Impl { // Finally assign material properties if geometry is a triangle mesh if (tmesh && tmesh->materials_.size() > 0) { - // Only a single material is supported for TriangleMesh so we - // just grab the first one we find. Users should be using - // TriangleMeshModel if they have a model with multiple - // materials. - auto &mesh_material = tmesh->materials_.begin()->second; + std::size_t material_index; + if (tmesh->HasTriangleMaterialIds()) { + auto minmax_it = std::minmax_element( + tmesh->triangle_material_ids_.begin(), + tmesh->triangle_material_ids_.end()); + if (*minmax_it.first != *minmax_it.second) { + utility::LogWarning( + "Only a single material is " + "supported for TriangleMesh visualization, " + "only the first referenced material will be " + "used. Use TriangleMeshModel if more than one " + "material is required."); + } + material_index = *minmax_it.first; + } else { + material_index = 0; + } + auto &mesh_material = tmesh->materials_[material_index].second; mat.base_color = {mesh_material.baseColor.r(), mesh_material.baseColor.g(), mesh_material.baseColor.b(), diff --git a/cpp/tests/t/geometry/TriangleMesh.cpp b/cpp/tests/t/geometry/TriangleMesh.cpp index 4e51b34e31d..520848a5032 100644 --- a/cpp/tests/t/geometry/TriangleMesh.cpp +++ b/cpp/tests/t/geometry/TriangleMesh.cpp @@ -394,7 +394,9 @@ TEST_P(TriangleMeshPermuteDevices, FromLegacy) { Eigen::Vector2d(0.4, 0.5), Eigen::Vector2d(0.6, 0.7), Eigen::Vector2d(0.8, 0.9), Eigen::Vector2d(1.0, 1.1)}; - auto& mat = legacy_mesh.materials_["Mat1"]; + legacy_mesh.materials_.emplace_back(); + legacy_mesh.materials_.front().first = "Mat1"; + auto& mat = legacy_mesh.materials_.front().second; mat.baseColor = mat.baseColor.CreateRGB(1, 1, 1); core::Dtype float_dtype = core::Float32; @@ -497,8 +499,11 @@ TEST_P(TriangleMeshPermuteDevices, ToLegacy) { Pointwise(FloatEq(), {0.8, 0.9}), Pointwise(FloatEq(), {1.0, 1.1})})); - EXPECT_TRUE(legacy_mesh.materials_.count("Mat1") > 0); - auto& mat = legacy_mesh.materials_["Mat1"]; + auto mat_iterator = std::find_if( + legacy_mesh.materials_.begin(), legacy_mesh.materials_.end(), + [](const auto& pair) -> bool { return pair.first == "Mat1"; }); + EXPECT_TRUE(mat_iterator != legacy_mesh.materials_.end()); + auto& mat = mat_iterator->second; EXPECT_TRUE(Eigen::Vector4f(mat.baseColor.f4) == Eigen::Vector4f(1, 1, 1, 1)); EXPECT_TRUE(mat.baseMetallic == 0.0);