From 55160da49d1aa1daa06c1fccfa3350755c89feb8 Mon Sep 17 00:00:00 2001 From: Rene Sepulveda Date: Fri, 29 Sep 2023 00:05:42 -0400 Subject: [PATCH 01/23] Initial implementation of glb2 export for tgeometry --- 3rdparty/assimp/assimp.cmake | 1 - cpp/open3d/t/io/TriangleMeshIO.cpp | 1 + cpp/open3d/t/io/TriangleMeshIO.h | 10 +++ cpp/open3d/t/io/file_format/FileASSIMP.cpp | 76 ++++++++++++++++++++++ 4 files changed, 87 insertions(+), 1 deletion(-) diff --git a/3rdparty/assimp/assimp.cmake b/3rdparty/assimp/assimp.cmake index 445f363dcb2..996a64400f3 100644 --- a/3rdparty/assimp/assimp.cmake +++ b/3rdparty/assimp/assimp.cmake @@ -26,7 +26,6 @@ ExternalProject_Add( -DCMAKE_CXX_FLAGS:STRING=${assimp_cmake_cxx_flags} -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX= - -DASSIMP_NO_EXPORT=ON -DASSIMP_BUILD_ASSIMP_TOOLS=OFF -DASSIMP_BUILD_TESTS=OFF -DASSIMP_INSTALL_PDB=OFF diff --git a/cpp/open3d/t/io/TriangleMeshIO.cpp b/cpp/open3d/t/io/TriangleMeshIO.cpp index e4f82f1e42f..3f511eb984e 100644 --- a/cpp/open3d/t/io/TriangleMeshIO.cpp +++ b/cpp/open3d/t/io/TriangleMeshIO.cpp @@ -45,6 +45,7 @@ static const std::unordered_map< const bool)>> file_extension_to_trianglemesh_write_function{ {"npz", WriteTriangleMeshToNPZ}, + {"glb", WriteTriangleMeshUsingASSIMP}, }; std::shared_ptr CreateMeshFromFile( diff --git a/cpp/open3d/t/io/TriangleMeshIO.h b/cpp/open3d/t/io/TriangleMeshIO.h index ccdaf4cee47..c6c88cb21e0 100644 --- a/cpp/open3d/t/io/TriangleMeshIO.h +++ b/cpp/open3d/t/io/TriangleMeshIO.h @@ -58,6 +58,16 @@ bool ReadTriangleMeshFromNPZ(const std::string &filename, geometry::TriangleMesh &mesh, const open3d::io::ReadTriangleMeshOptions ¶ms); +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); + bool WriteTriangleMeshToNPZ(const std::string &filename, const geometry::TriangleMesh &mesh, const bool write_ascii, diff --git a/cpp/open3d/t/io/file_format/FileASSIMP.cpp b/cpp/open3d/t/io/file_format/FileASSIMP.cpp index 0d5116cabb8..77321773731 100644 --- a/cpp/open3d/t/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/t/io/file_format/FileASSIMP.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -174,6 +175,81 @@ bool ReadTriangleMeshUsingASSIMP( return true; } +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) { + utility::LogWarning("Writing {} to GLB file using ASSIMP!!!", filename); + + // Sanity checks... + if (write_ascii) { + utility::LogWarning( + "TriangleMesh can't be saved in ASCII fromat as .glb"); + return false; + } + if (compressed) { + utility::LogWarning( + "TriangleMesh can't be saved in compressed format as .glb"); + return false; + } + if (!mesh.HasVertexPositions()) { + utility::LogWarning("TriangleMesh has no vertex positions and can't be saved as .glb"); + return false; + } + + Assimp::Exporter exporter; + auto ai_scene = new aiScene; + + // Fill mesh data... + ai_scene->mNumMeshes = 1; + ai_scene->mMeshes = new aiMesh *[1]; + auto ai_mesh = new aiMesh; + ai_mesh->mName.Set("Object1"); + auto vertices = mesh.GetVertexPositions(); + auto indices = mesh.GetTriangleIndices().To(core::Dtype::UInt32); + ai_mesh->mNumVertices = vertices.GetShape(0); + ai_mesh->mVertices = new aiVector3D[ai_mesh->mNumVertices]; + memcpy(&ai_mesh->mVertices->x, vertices.GetDataPtr(), sizeof(float)*ai_mesh->mNumVertices*3); + utility::LogWarning("Shape dim 0: {}", vertices.GetShape(0)); + utility::LogWarning("Shape dim 0 of indices: {}", indices.GetShape(0)); + utility::LogWarning("Shape dim 1 of indices: {}", indices.GetShape(1)); + ai_mesh->mNumFaces = indices.GetShape(0); + ai_mesh->mFaces = new aiFace[ai_mesh->mNumFaces]; + for (unsigned int i = 0; i < ai_mesh->mNumFaces; ++i) { + ai_mesh->mFaces[i].mNumIndices = 3; + ai_mesh->mFaces[i].mIndices = new unsigned int[3]; // triangles + ai_mesh->mFaces[i].mIndices[0] = indices[i][0].Item(); + ai_mesh->mFaces[i].mIndices[1] = indices[i][1].Item(); + ai_mesh->mFaces[i].mIndices[2] = indices[i][2].Item(); + } + ai_scene->mMeshes[0] = ai_mesh; + + // Fill material data... + ai_scene->mNumMaterials = 1; + ai_scene->mMaterials = new aiMaterial *[ai_scene->mNumMaterials]; + ai_scene->mMaterials[0] = new aiMaterial; + + auto root_node = new aiNode; + root_node->mName.Set("root"); + root_node->mNumMeshes = 1; + root_node->mMeshes = new unsigned int[root_node->mNumMeshes]; + root_node->mMeshes[0] = 0; + ai_scene->mRootNode = root_node; + + // Export + if (exporter.Export(ai_scene, "glb", filename.c_str()) == AI_FAILURE) { + utility::LogWarning("Got error: {}", exporter.GetErrorString()); + return false; + } + + return true; +} + } // namespace io } // namespace t } // namespace open3d From b7f792d82caf1304c66e063d20299c414bbd7d03 Mon Sep 17 00:00:00 2001 From: Rene Sepulveda Date: Mon, 2 Oct 2023 12:12:16 -0400 Subject: [PATCH 02/23] Add primitive type to prevent exporter crash --- cpp/open3d/t/io/file_format/FileASSIMP.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpp/open3d/t/io/file_format/FileASSIMP.cpp b/cpp/open3d/t/io/file_format/FileASSIMP.cpp index 77321773731..f522df8a79d 100644 --- a/cpp/open3d/t/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/t/io/file_format/FileASSIMP.cpp @@ -210,6 +210,8 @@ bool WriteTriangleMeshUsingASSIMP( ai_scene->mMeshes = new aiMesh *[1]; auto ai_mesh = new aiMesh; ai_mesh->mName.Set("Object1"); + ai_mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + // Guaranteed to have both vertex positions and triangle indices auto vertices = mesh.GetVertexPositions(); auto indices = mesh.GetTriangleIndices().To(core::Dtype::UInt32); ai_mesh->mNumVertices = vertices.GetShape(0); From 9cb2935984c927c2828717cab4a4427f77678f8f Mon Sep 17 00:00:00 2001 From: Rene Sepulveda Date: Mon, 2 Oct 2023 12:13:25 -0400 Subject: [PATCH 03/23] Add normals and uvs to scene for export --- cpp/open3d/t/io/file_format/FileASSIMP.cpp | 28 +++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/cpp/open3d/t/io/file_format/FileASSIMP.cpp b/cpp/open3d/t/io/file_format/FileASSIMP.cpp index f522df8a79d..814bd9b2182 100644 --- a/cpp/open3d/t/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/t/io/file_format/FileASSIMP.cpp @@ -229,6 +229,32 @@ bool WriteTriangleMeshUsingASSIMP( ai_mesh->mFaces[i].mIndices[1] = indices[i][1].Item(); ai_mesh->mFaces[i].mIndices[2] = indices[i][2].Item(); } + + // Add normals if present... + if (mesh.HasVertexNormals()) { + auto normals = mesh.GetVertexNormals(); + auto m_normals = normals.GetShape(0); + utility::LogWarning("Adding {} normals...", m_normals); + ai_mesh->mNormals = new aiVector3D[m_normals]; + memcpy(&ai_mesh->mNormals->x, normals.GetDataPtr(), sizeof(float)*m_normals*3); + } + + // Add UVs if present... + if (mesh.HasTriangleAttr("texture_uvs")) { + auto triangle_uvs = mesh.GetTriangleAttr("texture_uvs"); + auto vertex_uvs = core::Tensor::Empty( + {ai_mesh->mNumVertices, 2}, core::Dtype::Float32); + auto n_uvs = ai_mesh->mNumVertices; + utility::LogWarning("Adding {} texture uvs", n_uvs); + vertex_uvs.IndexGet({mesh.GetTriangleIndices()}) = triangle_uvs; + ai_mesh->mTextureCoords[0] = new aiVector3D[n_uvs]; + auto uv_ptr = reinterpret_cast(vertex_uvs.GetDataPtr()); + for (unsigned int i = 0; i < n_uvs; ++i) { + ai_mesh->mTextureCoords[0][i].x = *uv_ptr++; + ai_mesh->mTextureCoords[0][i].y = *uv_ptr++; + } + ai_mesh->mNumUVComponents[0] = 2; + } ai_scene->mMeshes[0] = ai_mesh; // Fill material data... @@ -244,7 +270,7 @@ bool WriteTriangleMeshUsingASSIMP( ai_scene->mRootNode = root_node; // Export - if (exporter.Export(ai_scene, "glb", filename.c_str()) == AI_FAILURE) { + if (exporter.Export(ai_scene, "glb2", filename.c_str()) == AI_FAILURE) { utility::LogWarning("Got error: {}", exporter.GetErrorString()); return false; } From 351482f663256db4ca3cd1fcd453bea42895a09a Mon Sep 17 00:00:00 2001 From: Rene Sepulveda Date: Mon, 2 Oct 2023 13:50:23 -0400 Subject: [PATCH 04/23] Save per-vertex color --- cpp/open3d/t/io/file_format/FileASSIMP.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/cpp/open3d/t/io/file_format/FileASSIMP.cpp b/cpp/open3d/t/io/file_format/FileASSIMP.cpp index 814bd9b2182..4dd195272fc 100644 --- a/cpp/open3d/t/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/t/io/file_format/FileASSIMP.cpp @@ -239,6 +239,25 @@ bool WriteTriangleMeshUsingASSIMP( memcpy(&ai_mesh->mNormals->x, normals.GetDataPtr(), sizeof(float)*m_normals*3); } + // Add colors if present... + if (mesh.HasVertexColors()) { + auto colors = mesh.GetVertexColors(); + auto m_colors = colors.GetShape(0); + utility::LogWarning("Adding {} colors...", m_colors); + ai_mesh->mColors[0] = new aiColor4D[m_colors]; + if (colors.GetShape(1) == 4) { + memcpy(&ai_mesh->mColors[0][0].r, colors.GetDataPtr(), sizeof(float)*m_colors*3); + } else { // must be 3 components + auto color_ptr = reinterpret_cast(colors.GetDataPtr()); + for (unsigned int i = 0; i < m_colors; ++i) { + ai_mesh->mColors[0][i].r = *color_ptr++; + ai_mesh->mColors[0][i].g = *color_ptr++; + ai_mesh->mColors[0][i].b = *color_ptr++; + ai_mesh->mColors[0][i].a = 1.0f; + } + } + } + // Add UVs if present... if (mesh.HasTriangleAttr("texture_uvs")) { auto triangle_uvs = mesh.GetTriangleAttr("texture_uvs"); From 56f906a9318d800355366f5d1ca3016c5412dd11 Mon Sep 17 00:00:00 2001 From: Rene Sepulveda Date: Mon, 2 Oct 2023 17:41:28 -0400 Subject: [PATCH 05/23] Get uv coords working correctly --- cpp/open3d/t/io/file_format/FileASSIMP.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/cpp/open3d/t/io/file_format/FileASSIMP.cpp b/cpp/open3d/t/io/file_format/FileASSIMP.cpp index 4dd195272fc..2bd9b75be96 100644 --- a/cpp/open3d/t/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/t/io/file_format/FileASSIMP.cpp @@ -184,8 +184,6 @@ bool WriteTriangleMeshUsingASSIMP( const bool write_vertex_colors, const bool write_triangle_uvs, const bool print_progress) { - utility::LogWarning("Writing {} to GLB file using ASSIMP!!!", filename); - // Sanity checks... if (write_ascii) { utility::LogWarning( @@ -217,9 +215,6 @@ bool WriteTriangleMeshUsingASSIMP( ai_mesh->mNumVertices = vertices.GetShape(0); ai_mesh->mVertices = new aiVector3D[ai_mesh->mNumVertices]; memcpy(&ai_mesh->mVertices->x, vertices.GetDataPtr(), sizeof(float)*ai_mesh->mNumVertices*3); - utility::LogWarning("Shape dim 0: {}", vertices.GetShape(0)); - utility::LogWarning("Shape dim 0 of indices: {}", indices.GetShape(0)); - utility::LogWarning("Shape dim 1 of indices: {}", indices.GetShape(1)); ai_mesh->mNumFaces = indices.GetShape(0); ai_mesh->mFaces = new aiFace[ai_mesh->mNumFaces]; for (unsigned int i = 0; i < ai_mesh->mNumFaces; ++i) { @@ -234,7 +229,6 @@ bool WriteTriangleMeshUsingASSIMP( if (mesh.HasVertexNormals()) { auto normals = mesh.GetVertexNormals(); auto m_normals = normals.GetShape(0); - utility::LogWarning("Adding {} normals...", m_normals); ai_mesh->mNormals = new aiVector3D[m_normals]; memcpy(&ai_mesh->mNormals->x, normals.GetDataPtr(), sizeof(float)*m_normals*3); } @@ -246,7 +240,7 @@ bool WriteTriangleMeshUsingASSIMP( utility::LogWarning("Adding {} colors...", m_colors); ai_mesh->mColors[0] = new aiColor4D[m_colors]; if (colors.GetShape(1) == 4) { - memcpy(&ai_mesh->mColors[0][0].r, colors.GetDataPtr(), sizeof(float)*m_colors*3); + memcpy(&ai_mesh->mColors[0][0].r, colors.GetDataPtr(), sizeof(float)*m_colors*4); } else { // must be 3 components auto color_ptr = reinterpret_cast(colors.GetDataPtr()); for (unsigned int i = 0; i < m_colors; ++i) { @@ -264,8 +258,11 @@ bool WriteTriangleMeshUsingASSIMP( auto vertex_uvs = core::Tensor::Empty( {ai_mesh->mNumVertices, 2}, core::Dtype::Float32); auto n_uvs = ai_mesh->mNumVertices; - utility::LogWarning("Adding {} texture uvs", n_uvs); - vertex_uvs.IndexGet({mesh.GetTriangleIndices()}) = triangle_uvs; + for (int64_t i = 0; i < indices.GetShape(0); i++) { + vertex_uvs[indices[i][0].Item()] = triangle_uvs[i][0]; + vertex_uvs[indices[i][1].Item()] = triangle_uvs[i][1]; + vertex_uvs[indices[i][2].Item()] = triangle_uvs[i][2]; + } ai_mesh->mTextureCoords[0] = new aiVector3D[n_uvs]; auto uv_ptr = reinterpret_cast(vertex_uvs.GetDataPtr()); for (unsigned int i = 0; i < n_uvs; ++i) { From 0db18c4acf946152a5f9977876c3e15acf544443 Mon Sep 17 00:00:00 2001 From: Rene Sepulveda Date: Mon, 2 Oct 2023 21:42:13 -0400 Subject: [PATCH 06/23] Remove debug printout --- cpp/open3d/t/io/file_format/FileASSIMP.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/cpp/open3d/t/io/file_format/FileASSIMP.cpp b/cpp/open3d/t/io/file_format/FileASSIMP.cpp index 2bd9b75be96..501a9e5ad87 100644 --- a/cpp/open3d/t/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/t/io/file_format/FileASSIMP.cpp @@ -237,7 +237,6 @@ bool WriteTriangleMeshUsingASSIMP( if (mesh.HasVertexColors()) { auto colors = mesh.GetVertexColors(); auto m_colors = colors.GetShape(0); - utility::LogWarning("Adding {} colors...", m_colors); ai_mesh->mColors[0] = new aiColor4D[m_colors]; if (colors.GetShape(1) == 4) { memcpy(&ai_mesh->mColors[0][0].r, colors.GetDataPtr(), sizeof(float)*m_colors*4); From b2b9c6ca71ab0caafe115dfb538a876bfbbe7fd9 Mon Sep 17 00:00:00 2001 From: Rene Sepulveda Date: Mon, 2 Oct 2023 21:47:25 -0400 Subject: [PATCH 07/23] Style fixes --- cpp/open3d/t/io/TriangleMeshIO.h | 17 ++++---- cpp/open3d/t/io/file_format/FileASSIMP.cpp | 49 ++++++++++++---------- 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/cpp/open3d/t/io/TriangleMeshIO.h b/cpp/open3d/t/io/TriangleMeshIO.h index c6c88cb21e0..a4d975d471a 100644 --- a/cpp/open3d/t/io/TriangleMeshIO.h +++ b/cpp/open3d/t/io/TriangleMeshIO.h @@ -58,15 +58,14 @@ bool ReadTriangleMeshFromNPZ(const std::string &filename, geometry::TriangleMesh &mesh, const open3d::io::ReadTriangleMeshOptions ¶ms); -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); +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); bool WriteTriangleMeshToNPZ(const std::string &filename, const geometry::TriangleMesh &mesh, diff --git a/cpp/open3d/t/io/file_format/FileASSIMP.cpp b/cpp/open3d/t/io/file_format/FileASSIMP.cpp index 501a9e5ad87..202a4fe9c87 100644 --- a/cpp/open3d/t/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/t/io/file_format/FileASSIMP.cpp @@ -9,8 +9,8 @@ #include #include -#include #include +#include #include #include #include @@ -175,15 +175,14 @@ bool ReadTriangleMeshUsingASSIMP( return true; } -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) { +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) { // Sanity checks... if (write_ascii) { utility::LogWarning( @@ -196,7 +195,9 @@ bool WriteTriangleMeshUsingASSIMP( return false; } if (!mesh.HasVertexPositions()) { - utility::LogWarning("TriangleMesh has no vertex positions and can't be saved as .glb"); + utility::LogWarning( + "TriangleMesh has no vertex positions and can't be saved as " + ".glb"); return false; } @@ -205,7 +206,7 @@ bool WriteTriangleMeshUsingASSIMP( // Fill mesh data... ai_scene->mNumMeshes = 1; - ai_scene->mMeshes = new aiMesh *[1]; + ai_scene->mMeshes = new aiMesh*[1]; auto ai_mesh = new aiMesh; ai_mesh->mName.Set("Object1"); ai_mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; @@ -214,12 +215,13 @@ bool WriteTriangleMeshUsingASSIMP( auto indices = mesh.GetTriangleIndices().To(core::Dtype::UInt32); ai_mesh->mNumVertices = vertices.GetShape(0); ai_mesh->mVertices = new aiVector3D[ai_mesh->mNumVertices]; - memcpy(&ai_mesh->mVertices->x, vertices.GetDataPtr(), sizeof(float)*ai_mesh->mNumVertices*3); + memcpy(&ai_mesh->mVertices->x, vertices.GetDataPtr(), + sizeof(float) * ai_mesh->mNumVertices * 3); ai_mesh->mNumFaces = indices.GetShape(0); ai_mesh->mFaces = new aiFace[ai_mesh->mNumFaces]; for (unsigned int i = 0; i < ai_mesh->mNumFaces; ++i) { ai_mesh->mFaces[i].mNumIndices = 3; - ai_mesh->mFaces[i].mIndices = new unsigned int[3]; // triangles + ai_mesh->mFaces[i].mIndices = new unsigned int[3]; // triangles ai_mesh->mFaces[i].mIndices[0] = indices[i][0].Item(); ai_mesh->mFaces[i].mIndices[1] = indices[i][1].Item(); ai_mesh->mFaces[i].mIndices[2] = indices[i][2].Item(); @@ -230,7 +232,8 @@ bool WriteTriangleMeshUsingASSIMP( auto normals = mesh.GetVertexNormals(); auto m_normals = normals.GetShape(0); ai_mesh->mNormals = new aiVector3D[m_normals]; - memcpy(&ai_mesh->mNormals->x, normals.GetDataPtr(), sizeof(float)*m_normals*3); + memcpy(&ai_mesh->mNormals->x, normals.GetDataPtr(), + sizeof(float) * m_normals * 3); } // Add colors if present... @@ -239,8 +242,9 @@ bool WriteTriangleMeshUsingASSIMP( auto m_colors = colors.GetShape(0); ai_mesh->mColors[0] = new aiColor4D[m_colors]; if (colors.GetShape(1) == 4) { - memcpy(&ai_mesh->mColors[0][0].r, colors.GetDataPtr(), sizeof(float)*m_colors*4); - } else { // must be 3 components + memcpy(&ai_mesh->mColors[0][0].r, colors.GetDataPtr(), + sizeof(float) * m_colors * 4); + } else { // must be 3 components auto color_ptr = reinterpret_cast(colors.GetDataPtr()); for (unsigned int i = 0; i < m_colors; ++i) { ai_mesh->mColors[0][i].r = *color_ptr++; @@ -254,8 +258,8 @@ bool WriteTriangleMeshUsingASSIMP( // Add UVs if present... if (mesh.HasTriangleAttr("texture_uvs")) { auto triangle_uvs = mesh.GetTriangleAttr("texture_uvs"); - auto vertex_uvs = core::Tensor::Empty( - {ai_mesh->mNumVertices, 2}, core::Dtype::Float32); + auto vertex_uvs = core::Tensor::Empty({ai_mesh->mNumVertices, 2}, + core::Dtype::Float32); auto n_uvs = ai_mesh->mNumVertices; for (int64_t i = 0; i < indices.GetShape(0); i++) { vertex_uvs[indices[i][0].Item()] = triangle_uvs[i][0]; @@ -274,8 +278,11 @@ bool WriteTriangleMeshUsingASSIMP( // Fill material data... ai_scene->mNumMaterials = 1; - ai_scene->mMaterials = new aiMaterial *[ai_scene->mNumMaterials]; - ai_scene->mMaterials[0] = new aiMaterial; + ai_scene->mMaterials = new aiMaterial*[ai_scene->mNumMaterials]; + auto ai_mat = new aiMaterial; + if (mesh.HasMaterial()) { + } + ai_scene->mMaterials[0] = ai_mat; auto root_node = new aiNode; root_node->mName.Set("root"); From a96da32a4c3b785fea0c92a03a8d9bf89008a8ca Mon Sep 17 00:00:00 2001 From: Rene Sepulveda Date: Thu, 5 Oct 2023 19:59:17 -0400 Subject: [PATCH 08/23] Skeleton of doing materials --- cpp/open3d/t/io/file_format/FileASSIMP.cpp | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/cpp/open3d/t/io/file_format/FileASSIMP.cpp b/cpp/open3d/t/io/file_format/FileASSIMP.cpp index 202a4fe9c87..4c32af6978c 100644 --- a/cpp/open3d/t/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/t/io/file_format/FileASSIMP.cpp @@ -281,6 +281,33 @@ bool WriteTriangleMeshUsingASSIMP(const std::string& filename, ai_scene->mMaterials = new aiMaterial*[ai_scene->mNumMaterials]; auto ai_mat = new aiMaterial; if (mesh.HasMaterial()) { + ai_mat->GetName().Set("mat1"); + auto shading_mode = aiShadingMode_PBR_BRDF; + ai_mat->AddProperty(&shading_mode, 1, AI_MATKEY_SHADING_MODEL); + + // Set base material properties + if (mesh.GetMaterial().HasBaseColor()) { + auto c = mesh.GetMaterial().GetBaseColor(); + auto ac = aiColor4D(c.x(), c.y(), c.z(), c.w()); + ai_mat->AddProperty(&ac, 1, AI_MATKEY_COLOR_DIFFUSE); + ai_mat->AddProperty(&ac, 1, AI_MATKEY_BASE_COLOR); + } + if (mesh.GetMaterial().HasBaseRoughness()) { + } + if (mesh.GetMaterial().HasBaseMetallic()) { + } + if (mesh.GetMaterial().HasBaseReflectance()) { + } + + // Now set texture maps + if (mesh.GetMaterial().HasAlbedoMap()) { + } + if (mesh.GetMaterial().HasRoughnessMap()) { + } + if (mesh.GetMaterial().HasMetallicMap()) { + } + if (mesh.GetMaterial().HasNormalMap()) { + } } ai_scene->mMaterials[0] = ai_mat; From de84c1f79cb0d3f4377c8f4b1416ea62f0651d25 Mon Sep 17 00:00:00 2001 From: Rene Sepulveda Date: Fri, 6 Oct 2023 10:37:49 -0400 Subject: [PATCH 09/23] Address review comments --- cpp/open3d/t/io/file_format/FileASSIMP.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/cpp/open3d/t/io/file_format/FileASSIMP.cpp b/cpp/open3d/t/io/file_format/FileASSIMP.cpp index 4c32af6978c..a131c797e8f 100644 --- a/cpp/open3d/t/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/t/io/file_format/FileASSIMP.cpp @@ -202,7 +202,7 @@ bool WriteTriangleMeshUsingASSIMP(const std::string& filename, } Assimp::Exporter exporter; - auto ai_scene = new aiScene; + auto ai_scene = std::unique_ptr(new aiScene); // Fill mesh data... ai_scene->mNumMeshes = 1; @@ -221,14 +221,19 @@ bool WriteTriangleMeshUsingASSIMP(const std::string& filename, ai_mesh->mFaces = new aiFace[ai_mesh->mNumFaces]; for (unsigned int i = 0; i < ai_mesh->mNumFaces; ++i) { ai_mesh->mFaces[i].mNumIndices = 3; + // NOTE: Yes, dynamically allocating 3 ints for each face is inefficient + // but this is what Assimp seems to require as it deletes each mIndices + // on destruction. We could block allocate space for all the faces, + // assign pointers here then zero out the pointers before destruction so + // the delete becomes a no-op, but that seems error prone. Could revisit + // if this becomes an IO bottleneck. ai_mesh->mFaces[i].mIndices = new unsigned int[3]; // triangles ai_mesh->mFaces[i].mIndices[0] = indices[i][0].Item(); ai_mesh->mFaces[i].mIndices[1] = indices[i][1].Item(); ai_mesh->mFaces[i].mIndices[2] = indices[i][2].Item(); } - // Add normals if present... - if (mesh.HasVertexNormals()) { + if (write_vertex_normals && mesh.HasVertexNormals()) { auto normals = mesh.GetVertexNormals(); auto m_normals = normals.GetShape(0); ai_mesh->mNormals = new aiVector3D[m_normals]; @@ -236,8 +241,7 @@ bool WriteTriangleMeshUsingASSIMP(const std::string& filename, sizeof(float) * m_normals * 3); } - // Add colors if present... - if (mesh.HasVertexColors()) { + if (write_vertex_colors && mesh.HasVertexColors()) { auto colors = mesh.GetVertexColors(); auto m_colors = colors.GetShape(0); ai_mesh->mColors[0] = new aiColor4D[m_colors]; @@ -255,8 +259,7 @@ bool WriteTriangleMeshUsingASSIMP(const std::string& filename, } } - // Add UVs if present... - if (mesh.HasTriangleAttr("texture_uvs")) { + if (write_triangle_uvs && mesh.HasTriangleAttr("texture_uvs")) { auto triangle_uvs = mesh.GetTriangleAttr("texture_uvs"); auto vertex_uvs = core::Tensor::Empty({ai_mesh->mNumVertices, 2}, core::Dtype::Float32); @@ -319,7 +322,8 @@ bool WriteTriangleMeshUsingASSIMP(const std::string& filename, ai_scene->mRootNode = root_node; // Export - if (exporter.Export(ai_scene, "glb2", filename.c_str()) == AI_FAILURE) { + if (exporter.Export(ai_scene.get(), "glb2", filename.c_str()) == + AI_FAILURE) { utility::LogWarning("Got error: {}", exporter.GetErrorString()); return false; } From 21dbc5aee3375577eb044cf86b7b005dca4e3b4a Mon Sep 17 00:00:00 2001 From: Rene Sepulveda Date: Sat, 14 Oct 2023 19:31:38 -0400 Subject: [PATCH 10/23] Ignore models when setting point and line sizes --- cpp/open3d/visualization/visualizer/O3DVisualizer.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cpp/open3d/visualization/visualizer/O3DVisualizer.cpp b/cpp/open3d/visualization/visualizer/O3DVisualizer.cpp index 5dc25714392..dfbffabf2fe 100644 --- a/cpp/open3d/visualization/visualizer/O3DVisualizer.cpp +++ b/cpp/open3d/visualization/visualizer/O3DVisualizer.cpp @@ -1521,6 +1521,9 @@ struct O3DVisualizer::Impl { px = int(ConvertToScaledPixels(px)); for (auto &o : objects_) { + // Ignore Models since they can never be point clouds + if (o.model) continue; + o.material.point_size = float(px); OverrideMaterial(o.name, o.material, ui_state_.scene_shader); } @@ -1546,6 +1549,9 @@ struct O3DVisualizer::Impl { px = int(ConvertToScaledPixels(px)); for (auto &o : objects_) { + // Ignore Models since they can never be point clouds + if (o.model) continue; + o.material.line_width = float(px); OverrideMaterial(o.name, o.material, ui_state_.scene_shader); } From 02486b167e1dc927165f5302d32bb0e030390423 Mon Sep 17 00:00:00 2001 From: Rene Sepulveda Date: Sat, 14 Oct 2023 20:27:22 -0400 Subject: [PATCH 11/23] Export base material properties --- cpp/open3d/t/io/file_format/FileASSIMP.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/cpp/open3d/t/io/file_format/FileASSIMP.cpp b/cpp/open3d/t/io/file_format/FileASSIMP.cpp index a131c797e8f..92aa453be94 100644 --- a/cpp/open3d/t/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/t/io/file_format/FileASSIMP.cpp @@ -296,10 +296,29 @@ bool WriteTriangleMeshUsingASSIMP(const std::string& filename, ai_mat->AddProperty(&ac, 1, AI_MATKEY_BASE_COLOR); } if (mesh.GetMaterial().HasBaseRoughness()) { + auto r = mesh.GetMaterial().GetBaseRoughness(); + ai_mat->AddProperty(&r, 1, AI_MATKEY_ROUGHNESS_FACTOR); } if (mesh.GetMaterial().HasBaseMetallic()) { + auto m = mesh.GetMaterial().GetBaseMetallic(); + ai_mat->AddProperty(&m, 1, AI_MATKEY_METALLIC_FACTOR); } if (mesh.GetMaterial().HasBaseReflectance()) { + auto r = mesh.GetMaterial().GetBaseReflectance(); + ai_mat->AddProperty(&r, 1, AI_MATKEY_REFLECTIVITY); + ai_mat->AddProperty(&r, 1, AI_MATKEY_SHEEN); + } + if (mesh.GetMaterial().HasAnisotropy()) { + auto a = mesh.GetMaterial().GetAnisotropy(); + ai_mat->AddProperty(&a, 1, AI_MATKEY_ANISOTROPY); + } + if (mesh.GetMaterial().HasBaseClearcoat()) { + auto c = mesh.GetMaterial().GetBaseClearcoat(); + ai_mat->AddProperty(&c, 1, AI_MATKEY_CLEARCOAT_FACTOR); + } + if (mesh.GetMaterial().HasBaseClearcoatRoughness()) { + auto r = mesh.GetMaterial().GetBaseClearcoatRoughness(); + ai_mat->AddProperty(&r, 1, AI_MATKEY_CLEARCOAT_ROUGHNESS); } // Now set texture maps From 05a7a64a65346c28589d8fdf56eed9017bdf885f Mon Sep 17 00:00:00 2001 From: Rene Sepulveda Date: Sat, 14 Oct 2023 20:28:23 -0400 Subject: [PATCH 12/23] Import clear coat using new Assimp key as backup --- cpp/open3d/io/file_format/FileASSIMP.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/open3d/io/file_format/FileASSIMP.cpp b/cpp/open3d/io/file_format/FileASSIMP.cpp index dec40259566..a877bc90770 100644 --- a/cpp/open3d/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/io/file_format/FileASSIMP.cpp @@ -414,6 +414,7 @@ bool ReadModelUsingAssimp(const std::string& filename, mat->Get(AI_MATKEY_SHEEN, o3d_mat.base_reflectance); mat->Get(AI_MATKEY_CLEARCOAT_THICKNESS, o3d_mat.base_clearcoat); + mat->Get(AI_MATKEY_CLEARCOAT_FACTOR, o3d_mat.base_clearcoat); mat->Get(AI_MATKEY_CLEARCOAT_ROUGHNESS, o3d_mat.base_clearcoat_roughness); mat->Get(AI_MATKEY_ANISOTROPY, o3d_mat.base_anisotropy); From ce47a25411b962df0403a6044d1897221bc4ce87 Mon Sep 17 00:00:00 2001 From: Rene Sepulveda Date: Sun, 15 Oct 2023 20:04:29 -0400 Subject: [PATCH 13/23] Fix bug importing clear coat roughness --- cpp/open3d/io/file_format/FileASSIMP.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/open3d/io/file_format/FileASSIMP.cpp b/cpp/open3d/io/file_format/FileASSIMP.cpp index a877bc90770..747ead760f6 100644 --- a/cpp/open3d/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/io/file_format/FileASSIMP.cpp @@ -415,7 +415,7 @@ bool ReadModelUsingAssimp(const std::string& filename, mat->Get(AI_MATKEY_CLEARCOAT_THICKNESS, o3d_mat.base_clearcoat); mat->Get(AI_MATKEY_CLEARCOAT_FACTOR, o3d_mat.base_clearcoat); - mat->Get(AI_MATKEY_CLEARCOAT_ROUGHNESS, + mat->Get(AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR, o3d_mat.base_clearcoat_roughness); mat->Get(AI_MATKEY_ANISOTROPY, o3d_mat.base_anisotropy); aiString alpha_mode; From 02266d8f3a837d78de5404be1ab9e625ab7c3d98 Mon Sep 17 00:00:00 2001 From: Rene Sepulveda Date: Sun, 15 Oct 2023 20:04:46 -0400 Subject: [PATCH 14/23] Remove properties not supported for export --- cpp/open3d/t/io/file_format/FileASSIMP.cpp | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/cpp/open3d/t/io/file_format/FileASSIMP.cpp b/cpp/open3d/t/io/file_format/FileASSIMP.cpp index 92aa453be94..d8fe44db77c 100644 --- a/cpp/open3d/t/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/t/io/file_format/FileASSIMP.cpp @@ -289,6 +289,8 @@ bool WriteTriangleMeshUsingASSIMP(const std::string& filename, ai_mat->AddProperty(&shading_mode, 1, AI_MATKEY_SHADING_MODEL); // Set base material properties + // NOTE: not all properties supported by Open3D are supported by Assimp. + // Those properties (reflectivity, anisotropy) are not exported if (mesh.GetMaterial().HasBaseColor()) { auto c = mesh.GetMaterial().GetBaseColor(); auto ac = aiColor4D(c.x(), c.y(), c.z(), c.w()); @@ -303,22 +305,13 @@ bool WriteTriangleMeshUsingASSIMP(const std::string& filename, auto m = mesh.GetMaterial().GetBaseMetallic(); ai_mat->AddProperty(&m, 1, AI_MATKEY_METALLIC_FACTOR); } - if (mesh.GetMaterial().HasBaseReflectance()) { - auto r = mesh.GetMaterial().GetBaseReflectance(); - ai_mat->AddProperty(&r, 1, AI_MATKEY_REFLECTIVITY); - ai_mat->AddProperty(&r, 1, AI_MATKEY_SHEEN); - } - if (mesh.GetMaterial().HasAnisotropy()) { - auto a = mesh.GetMaterial().GetAnisotropy(); - ai_mat->AddProperty(&a, 1, AI_MATKEY_ANISOTROPY); - } if (mesh.GetMaterial().HasBaseClearcoat()) { auto c = mesh.GetMaterial().GetBaseClearcoat(); ai_mat->AddProperty(&c, 1, AI_MATKEY_CLEARCOAT_FACTOR); } if (mesh.GetMaterial().HasBaseClearcoatRoughness()) { auto r = mesh.GetMaterial().GetBaseClearcoatRoughness(); - ai_mat->AddProperty(&r, 1, AI_MATKEY_CLEARCOAT_ROUGHNESS); + ai_mat->AddProperty(&r, 1, AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR); } // Now set texture maps From 4222b1b14af7ae6054d9be84425e777d09d2e91d Mon Sep 17 00:00:00 2001 From: Rene Sepulveda Date: Mon, 16 Oct 2023 19:19:08 -0400 Subject: [PATCH 15/23] Add function to encode image to PNG in memory --- cpp/open3d/t/io/ImageIO.h | 4 +++ cpp/open3d/t/io/file_format/FilePNG.cpp | 41 +++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/cpp/open3d/t/io/ImageIO.h b/cpp/open3d/t/io/ImageIO.h index 211dac41325..bd373ed64be 100644 --- a/cpp/open3d/t/io/ImageIO.h +++ b/cpp/open3d/t/io/ImageIO.h @@ -61,6 +61,10 @@ bool WriteImageToPNG(const std::string &filename, const geometry::Image &image, int quality = kOpen3DImageIODefaultQuality); +bool WriteImageToPNGInMemory(std::vector& output_buffer, + const geometry::Image &image, + int quality = kOpen3DImageIODefaultQuality); + bool ReadImageFromJPG(const std::string &filename, geometry::Image &image); bool WriteImageToJPG(const std::string &filename, diff --git a/cpp/open3d/t/io/file_format/FilePNG.cpp b/cpp/open3d/t/io/file_format/FilePNG.cpp index 2c4e40a9f48..3981f4ec351 100644 --- a/cpp/open3d/t/io/file_format/FilePNG.cpp +++ b/cpp/open3d/t/io/file_format/FilePNG.cpp @@ -103,6 +103,47 @@ bool WriteImageToPNG(const std::string &filename, return true; } +bool WriteImageToPNGInMemory(std::vector& buffer, + const t::geometry::Image &image, + int quality) { + if (image.IsEmpty()) { + utility::LogWarning("Write PNG failed: image has no data."); + return false; + } + if (image.GetDtype() != core::UInt8 && image.GetDtype() != core::UInt16) { + utility::LogWarning("Write PNG failed: unsupported image data."); + return false; + } + if (quality == kOpen3DImageIODefaultQuality) // Set default quality + { + quality = 6; + } + if (quality < 0 || quality > 9) { + utility::LogWarning( + "Write PNG failed: quality ({}) must be in the range [0,9]", + quality); + return false; + } + png_image pngimage; + memset(&pngimage, 0, sizeof(pngimage)); + pngimage.version = PNG_IMAGE_VERSION; + SetPNGImageFromImage(image, quality, pngimage); + + // Compute bytes required + size_t mem_bytes = 0; + if (png_image_write_to_memory(&pngimage, nullptr, &mem_bytes, 0, image.GetDataPtr(), 0, nullptr) == 0) { + utility::LogWarning("Could not compute bytes needed for encoding to PNG in memory."); + return false; + } + utility::LogWarning("The bytes needed to encode this png: {}", mem_bytes); + buffer.resize(mem_bytes); + if (png_image_write_to_memory(&pngimage, &buffer[0], &mem_bytes, 0, image.GetDataPtr(), 0, nullptr) == 0) { + utility::LogWarning("Unable to encode to encode to PNG in memory."); + return false; + } + return true; +} + } // namespace io } // namespace t } // namespace open3d From d0c5fe73747c07a2d3f0288c0975545642dc8cc0 Mon Sep 17 00:00:00 2001 From: Rene Sepulveda Date: Tue, 17 Oct 2023 10:10:33 -0400 Subject: [PATCH 16/23] Support assimp's PBR BASE_COLOR map --- cpp/open3d/io/file_format/FileASSIMP.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/cpp/open3d/io/file_format/FileASSIMP.cpp b/cpp/open3d/io/file_format/FileASSIMP.cpp index 747ead760f6..34b214ec94a 100644 --- a/cpp/open3d/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/io/file_format/FileASSIMP.cpp @@ -71,6 +71,7 @@ void LoadTextures(const std::string& filename, if (mat->GetTextureCount(type) > 0) { aiString path; mat->GetTexture(type, 0, &path); + // If the texture is an embedded texture, use `GetEmbeddedTexture`. if (auto texture = scene->GetEmbeddedTexture(path.C_Str())) { if (texture->CheckFormat("png")) { @@ -91,13 +92,10 @@ void LoadTextures(const std::string& filename, if (image->HasData()) { img = image; } - } - - else { + } else { utility::LogWarning( "This format of image is not supported."); } - } // Else, build the path to it. else { @@ -122,7 +120,12 @@ void LoadTextures(const std::string& filename, } }; - texture_loader(aiTextureType_DIFFUSE, maps.albedo); + // Prefer BASE_COLOR texture as assimp now uses it for PBR workflows + if (mat->GetTextureCount(aiTextureType_BASE_COLOR) > 0) { + texture_loader(aiTextureType_BASE_COLOR, maps.albedo); + } else { + texture_loader(aiTextureType_DIFFUSE, maps.albedo); + } texture_loader(aiTextureType_NORMALS, maps.normal); // Assimp may place ambient occlusion texture in AMBIENT_OCCLUSION if // format has AO support. Prefer that texture if it is preset. Otherwise, From 8da0f81ab256b5558758eb808b6095a31ac49c22 Mon Sep 17 00:00:00 2001 From: Rene Sepulveda Date: Tue, 17 Oct 2023 10:11:11 -0400 Subject: [PATCH 17/23] Add write png to memory function --- cpp/open3d/t/io/file_format/FilePNG.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/cpp/open3d/t/io/file_format/FilePNG.cpp b/cpp/open3d/t/io/file_format/FilePNG.cpp index 3981f4ec351..2497e2a71c8 100644 --- a/cpp/open3d/t/io/file_format/FilePNG.cpp +++ b/cpp/open3d/t/io/file_format/FilePNG.cpp @@ -103,9 +103,9 @@ bool WriteImageToPNG(const std::string &filename, return true; } -bool WriteImageToPNGInMemory(std::vector& buffer, - const t::geometry::Image &image, - int quality) { +bool WriteImageToPNGInMemory(std::vector &buffer, + const t::geometry::Image &image, + int quality) { if (image.IsEmpty()) { utility::LogWarning("Write PNG failed: image has no data."); return false; @@ -131,13 +131,16 @@ bool WriteImageToPNGInMemory(std::vector& buffer, // Compute bytes required size_t mem_bytes = 0; - if (png_image_write_to_memory(&pngimage, nullptr, &mem_bytes, 0, image.GetDataPtr(), 0, nullptr) == 0) { - utility::LogWarning("Could not compute bytes needed for encoding to PNG in memory."); + if (png_image_write_to_memory(&pngimage, nullptr, &mem_bytes, 0, + image.GetDataPtr(), 0, nullptr) == 0) { + utility::LogWarning( + "Could not compute bytes needed for encoding to PNG in " + "memory."); return false; } - utility::LogWarning("The bytes needed to encode this png: {}", mem_bytes); buffer.resize(mem_bytes); - if (png_image_write_to_memory(&pngimage, &buffer[0], &mem_bytes, 0, image.GetDataPtr(), 0, nullptr) == 0) { + if (png_image_write_to_memory(&pngimage, &buffer[0], &mem_bytes, 0, + image.GetDataPtr(), 0, nullptr) == 0) { utility::LogWarning("Unable to encode to encode to PNG in memory."); return false; } From a0c4a8c225e7b46e47f9b7cd67a57dea00d84d2c Mon Sep 17 00:00:00 2001 From: Rene Sepulveda Date: Tue, 17 Oct 2023 10:11:30 -0400 Subject: [PATCH 18/23] Apply style --- cpp/open3d/t/io/ImageIO.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/open3d/t/io/ImageIO.h b/cpp/open3d/t/io/ImageIO.h index bd373ed64be..6a7edd6e17b 100644 --- a/cpp/open3d/t/io/ImageIO.h +++ b/cpp/open3d/t/io/ImageIO.h @@ -61,9 +61,9 @@ bool WriteImageToPNG(const std::string &filename, const geometry::Image &image, int quality = kOpen3DImageIODefaultQuality); -bool WriteImageToPNGInMemory(std::vector& output_buffer, - const geometry::Image &image, - int quality = kOpen3DImageIODefaultQuality); +bool WriteImageToPNGInMemory(std::vector &output_buffer, + const geometry::Image &image, + int quality = kOpen3DImageIODefaultQuality); bool ReadImageFromJPG(const std::string &filename, geometry::Image &image); From 146288d8973f7d6a69c28c7e67b9db59f020fbb6 Mon Sep 17 00:00:00 2001 From: Rene Sepulveda Date: Tue, 17 Oct 2023 10:11:42 -0400 Subject: [PATCH 19/23] Complete export of texture maps --- cpp/open3d/t/io/file_format/FileASSIMP.cpp | 124 ++++++++++++++++++++- 1 file changed, 120 insertions(+), 4 deletions(-) diff --git a/cpp/open3d/t/io/file_format/FileASSIMP.cpp b/cpp/open3d/t/io/file_format/FileASSIMP.cpp index d8fe44db77c..c2694bb9289 100644 --- a/cpp/open3d/t/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/t/io/file_format/FileASSIMP.cpp @@ -18,6 +18,7 @@ #include "open3d/core/ParallelFor.h" #include "open3d/core/TensorFunction.h" +#include "open3d/t/io/ImageIO.h" #include "open3d/t/io/TriangleMeshIO.h" #include "open3d/utility/FileSystem.h" #include "open3d/utility/Logging.h" @@ -175,6 +176,37 @@ bool ReadTriangleMeshUsingASSIMP( return true; } +void SetTextureMaterialProperty(aiMaterial* mat, + aiScene* scene, + int texture_idx, + aiTextureType tt, + t::geometry::Image& img) { + // Encode image as PNG + std::vector img_buffer; + WriteImageToPNGInMemory(img_buffer, img, 6); + + // Fill in Assimp's texture class and add to its material + auto tex = scene->mTextures[texture_idx]; + std::string tex_id("*"); + tex_id += std::to_string(texture_idx); + tex->mFilename = tex_id.c_str(); + tex->mHeight = 0; + tex->mWidth = img_buffer.size(); + // NOTE: Assimp takes ownership of the data so we need to copy it + // into a separate buffer that Assimp can take care of delete []-ing + uint8_t* img_data = new uint8_t[img_buffer.size()]; + memcpy(img_data, img_buffer.data(), img_buffer.size()); + tex->pcData = reinterpret_cast(img_data); + strcpy(tex->achFormatHint, "png"); + aiString uri(tex_id); + const int uv_index = 0; + const aiTextureMapMode mode = aiTextureMapMode_Wrap; + mat->AddProperty(&uri, AI_MATKEY_TEXTURE(tt, 0)); + mat->AddProperty(&uv_index, 1, AI_MATKEY_UVWSRC(tt, 0)); + mat->AddProperty(&mode, 1, AI_MATKEY_MAPPINGMODE_U(tt, 0)); + mat->AddProperty(&mode, 1, AI_MATKEY_MAPPINGMODE_V(tt, 0)); +} + bool WriteTriangleMeshUsingASSIMP(const std::string& filename, const geometry::TriangleMesh& mesh, const bool write_ascii, @@ -314,14 +346,98 @@ bool WriteTriangleMeshUsingASSIMP(const std::string& filename, ai_mat->AddProperty(&r, 1, AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR); } - // Now set texture maps - if (mesh.GetMaterial().HasAlbedoMap()) { + // Count texture maps... + // NOTE: GLTF2 expects a single combined roughness/metal map. If the + // model has one we just export it, otherwise if both roughness and + // metal maps are avaialbe we combine them, otherwise if only one or the + // other is available we just export the one map. + int n_textures = 0; + if (mesh.GetMaterial().HasAlbedoMap()) ++n_textures; + if (mesh.GetMaterial().HasNormalMap()) ++n_textures; + if (mesh.GetMaterial().HasAORoughnessMetalMap()) { + ++n_textures; + } else if (mesh.GetMaterial().HasRoughnessMap() && + mesh.GetMaterial().HasMetallicMap()) { + ++n_textures; + } else { + if (mesh.GetMaterial().HasRoughnessMap()) ++n_textures; + if (mesh.GetMaterial().HasMetallicMap()) ++n_textures; } - if (mesh.GetMaterial().HasRoughnessMap()) { + if (n_textures > 0) { + ai_scene->mTextures = new aiTexture*[n_textures]; + for (int i = 0; i < n_textures; ++i) { + ai_scene->mTextures[i] = new aiTexture(); + } + ai_scene->mNumTextures = n_textures; } - if (mesh.GetMaterial().HasMetallicMap()) { + + // Now embed the textures that are available... + int current_idx = 0; + if (mesh.GetMaterial().HasAlbedoMap()) { + auto img = mesh.GetMaterial().GetAlbedoMap(); + SetTextureMaterialProperty(ai_mat, ai_scene.get(), current_idx, + aiTextureType_DIFFUSE, img); + SetTextureMaterialProperty(ai_mat, ai_scene.get(), current_idx, + aiTextureType_BASE_COLOR, img); + ++current_idx; + } + if (mesh.GetMaterial().HasAORoughnessMetalMap()) { + auto img = mesh.GetMaterial().GetAORoughnessMetalMap(); + SetTextureMaterialProperty(ai_mat, ai_scene.get(), current_idx, + aiTextureType_UNKNOWN, img); + ++current_idx; + } else if (mesh.GetMaterial().HasRoughnessMap() && + mesh.GetMaterial().HasMetallicMap()) { + auto rough = mesh.GetMaterial().GetRoughnessMap(); + auto metal = mesh.GetMaterial().GetMetallicMap(); + auto rows = rough.GetRows(); + auto cols = rough.GetCols(); + auto rough_metal = + geometry::Image(rows, cols, 4, core::Dtype::UInt8); + rough_metal.AsTensor() = + core::Tensor::Ones(rough_metal.AsTensor().GetShape(), + core::Dtype::UInt8) * + 255; + auto metal_channel = metal.AsTensor().GetItem( + {core::TensorKey::Slice(0, rows + 1, core::None), + core::TensorKey::Slice(0, cols + 1, core::None), + core::TensorKey::Index(0)}); + auto rough_channel = rough.AsTensor().GetItem( + {core::TensorKey::Slice(0, rows + 1, core::None), + core::TensorKey::Slice(0, cols + 1, core::None), + core::TensorKey::Index(0)}); + rough_metal.AsTensor().SetItem( + {core::TensorKey::Slice(0, rows + 1, core::None), + core::TensorKey::Slice(0, cols + 1, core::None), + core::TensorKey::Index(2)}, + metal_channel); + rough_metal.AsTensor().SetItem( + {core::TensorKey::Slice(0, rows + 1, core::None), + core::TensorKey::Slice(0, cols + 1, core::None), + core::TensorKey::Index(1)}, + rough_channel); + SetTextureMaterialProperty(ai_mat, ai_scene.get(), current_idx, + aiTextureType_UNKNOWN, rough_metal); + ++current_idx; + } else { + if (mesh.GetMaterial().HasRoughnessMap()) { + auto img = mesh.GetMaterial().GetRoughnessMap(); + SetTextureMaterialProperty(ai_mat, ai_scene.get(), current_idx, + aiTextureType_UNKNOWN, img); + ++current_idx; + } + if (mesh.GetMaterial().HasMetallicMap()) { + auto img = mesh.GetMaterial().GetMetallicMap(); + SetTextureMaterialProperty(ai_mat, ai_scene.get(), current_idx, + aiTextureType_UNKNOWN, img); + ++current_idx; + } } if (mesh.GetMaterial().HasNormalMap()) { + auto img = mesh.GetMaterial().GetNormalMap(); + SetTextureMaterialProperty(ai_mat, ai_scene.get(), current_idx, + aiTextureType_NORMALS, img); + ++current_idx; } } ai_scene->mMaterials[0] = ai_mat; From 2aea50b92ab53a770f017e5c525ad7d8bafdd6b9 Mon Sep 17 00:00:00 2001 From: Rene Sepulveda Date: Tue, 17 Oct 2023 20:10:47 -0400 Subject: [PATCH 20/23] Warn if mesh has triangle normals or colors --- cpp/open3d/t/io/file_format/FileASSIMP.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cpp/open3d/t/io/file_format/FileASSIMP.cpp b/cpp/open3d/t/io/file_format/FileASSIMP.cpp index c2694bb9289..53f5b974d2c 100644 --- a/cpp/open3d/t/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/t/io/file_format/FileASSIMP.cpp @@ -232,6 +232,17 @@ bool WriteTriangleMeshUsingASSIMP(const std::string& filename, ".glb"); return false; } + // Check for unsupported features + if (mesh.HasTriangleNormals()) { + utility::LogWarning( + "Exporting triangle normals is not supported. Please convert " + "to vertex normals or export to a format that supports it."); + } + if (mesh.HasTriangleColors()) { + utility::LogWarning( + "Exporting triangle colors is not supported. Please convert to " + "vertex colors or export to a format that supporst it."); + } Assimp::Exporter exporter; auto ai_scene = std::unique_ptr(new aiScene); From f9937f8766fe3015809edef09bdc20896e0aa83a Mon Sep 17 00:00:00 2001 From: Rene Sepulveda Date: Tue, 17 Oct 2023 20:16:00 -0400 Subject: [PATCH 21/23] Make sure Tensors are contiguous before memcpy from them --- cpp/open3d/t/io/file_format/FileASSIMP.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cpp/open3d/t/io/file_format/FileASSIMP.cpp b/cpp/open3d/t/io/file_format/FileASSIMP.cpp index 53f5b974d2c..f05378fc278 100644 --- a/cpp/open3d/t/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/t/io/file_format/FileASSIMP.cpp @@ -254,8 +254,9 @@ bool WriteTriangleMeshUsingASSIMP(const std::string& filename, ai_mesh->mName.Set("Object1"); ai_mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; // Guaranteed to have both vertex positions and triangle indices - auto vertices = mesh.GetVertexPositions(); - auto indices = mesh.GetTriangleIndices().To(core::Dtype::UInt32); + auto vertices = mesh.GetVertexPositions().Contiguous(); + auto indices = + mesh.GetTriangleIndices().Contiguous().To(core::Dtype::UInt32); ai_mesh->mNumVertices = vertices.GetShape(0); ai_mesh->mVertices = new aiVector3D[ai_mesh->mNumVertices]; memcpy(&ai_mesh->mVertices->x, vertices.GetDataPtr(), @@ -277,7 +278,7 @@ bool WriteTriangleMeshUsingASSIMP(const std::string& filename, } if (write_vertex_normals && mesh.HasVertexNormals()) { - auto normals = mesh.GetVertexNormals(); + auto normals = mesh.GetVertexNormals().Contiguous(); auto m_normals = normals.GetShape(0); ai_mesh->mNormals = new aiVector3D[m_normals]; memcpy(&ai_mesh->mNormals->x, normals.GetDataPtr(), @@ -285,7 +286,7 @@ bool WriteTriangleMeshUsingASSIMP(const std::string& filename, } if (write_vertex_colors && mesh.HasVertexColors()) { - auto colors = mesh.GetVertexColors(); + auto colors = mesh.GetVertexColors().Contiguous(); auto m_colors = colors.GetShape(0); ai_mesh->mColors[0] = new aiColor4D[m_colors]; if (colors.GetShape(1) == 4) { @@ -303,7 +304,7 @@ bool WriteTriangleMeshUsingASSIMP(const std::string& filename, } if (write_triangle_uvs && mesh.HasTriangleAttr("texture_uvs")) { - auto triangle_uvs = mesh.GetTriangleAttr("texture_uvs"); + auto triangle_uvs = mesh.GetTriangleAttr("texture_uvs").Contiguous(); auto vertex_uvs = core::Tensor::Empty({ai_mesh->mNumVertices, 2}, core::Dtype::Float32); auto n_uvs = ai_mesh->mNumVertices; From 6251a7184159d03ffd090af11aba8db2ccd9695a Mon Sep 17 00:00:00 2001 From: Rene Sepulveda Date: Tue, 31 Oct 2023 12:37:06 -0400 Subject: [PATCH 22/23] Swap order of operations to eliminate Tensor copy --- cpp/open3d/t/io/file_format/FileASSIMP.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/open3d/t/io/file_format/FileASSIMP.cpp b/cpp/open3d/t/io/file_format/FileASSIMP.cpp index f05378fc278..5f32a962483 100644 --- a/cpp/open3d/t/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/t/io/file_format/FileASSIMP.cpp @@ -256,7 +256,7 @@ bool WriteTriangleMeshUsingASSIMP(const std::string& filename, // Guaranteed to have both vertex positions and triangle indices auto vertices = mesh.GetVertexPositions().Contiguous(); auto indices = - mesh.GetTriangleIndices().Contiguous().To(core::Dtype::UInt32); + mesh.GetTriangleIndices().To(core::Dtype::UInt32).Contiguous(); ai_mesh->mNumVertices = vertices.GetShape(0); ai_mesh->mVertices = new aiVector3D[ai_mesh->mNumVertices]; memcpy(&ai_mesh->mVertices->x, vertices.GetDataPtr(), From 007348a62a2053766b710a0232a67e384c667e5d Mon Sep 17 00:00:00 2001 From: Rene Sepulveda Date: Wed, 1 Nov 2023 10:51:22 -0400 Subject: [PATCH 23/23] Make static to avoid symbol leakage --- cpp/open3d/t/io/file_format/FileASSIMP.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cpp/open3d/t/io/file_format/FileASSIMP.cpp b/cpp/open3d/t/io/file_format/FileASSIMP.cpp index 5f32a962483..5eae7a9ffdc 100644 --- a/cpp/open3d/t/io/file_format/FileASSIMP.cpp +++ b/cpp/open3d/t/io/file_format/FileASSIMP.cpp @@ -176,11 +176,11 @@ bool ReadTriangleMeshUsingASSIMP( return true; } -void SetTextureMaterialProperty(aiMaterial* mat, - aiScene* scene, - int texture_idx, - aiTextureType tt, - t::geometry::Image& img) { +static void SetTextureMaterialProperty(aiMaterial* mat, + aiScene* scene, + int texture_idx, + aiTextureType tt, + t::geometry::Image& img) { // Encode image as PNG std::vector img_buffer; WriteImageToPNGInMemory(img_buffer, img, 6);