Skip to content

Commit

Permalink
Convert triangle mesh model to tmesh map
Browse files Browse the repository at this point in the history
ToString for Material
MaterialRecord to Material conversion
Support for emissive color reading in FileASSIMP
 (model) and writing for tmesh (tio FIleASSIMP)
  • Loading branch information
ssheorey committed Apr 18, 2024
1 parent 785878f commit 4ae5390
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 7 deletions.
3 changes: 3 additions & 0 deletions cpp/open3d/io/file_format/FileASSIMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,9 @@ bool ReadModelUsingAssimp(const std::string& filename,
mat->Get(AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR,
o3d_mat.base_clearcoat_roughness);
mat->Get(AI_MATKEY_ANISOTROPY, o3d_mat.base_anisotropy);
mat->Get(AI_MATKEY_COLOR_EMISSIVE, color);
o3d_mat.emissive_color =
Eigen::Vector4f(color.r, color.g, color.b, 1.f);
aiString alpha_mode;
mat->Get(AI_MATKEY_GLTF_ALPHAMODE, alpha_mode);
std::string alpha_mode_str(alpha_mode.C_Str());
Expand Down
25 changes: 21 additions & 4 deletions cpp/open3d/t/geometry/TriangleMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ geometry::TriangleMesh TriangleMesh::FromLegacy(
tmat.SetAnisotropy(mat.baseAnisotropy);
tmat.SetBaseClearcoat(mat.baseClearCoat);
tmat.SetBaseClearcoatRoughness(mat.baseClearCoatRoughness);
// no emissive_color in legacy mesh material
if (mat.albedo) tmat.SetAlbedoMap(Image::FromLegacy(*mat.albedo));
if (mat.normalMap) tmat.SetNormalMap(Image::FromLegacy(*mat.normalMap));
if (mat.roughness)
Expand Down Expand Up @@ -453,10 +454,6 @@ open3d::geometry::TriangleMesh TriangleMesh::ToLegacy() const {
legacy_mat.baseColor.f4[1] = tmat.GetBaseColor().y();
legacy_mat.baseColor.f4[2] = tmat.GetBaseColor().z();
legacy_mat.baseColor.f4[3] = tmat.GetBaseColor().w();
utility::LogWarning("{},{},{},{}", legacy_mat.baseColor.f4[0],
legacy_mat.baseColor.f4[1],
legacy_mat.baseColor.f4[2],
legacy_mat.baseColor.f4[3]);
}
if (tmat.HasBaseRoughness()) {
legacy_mat.baseRoughness = tmat.GetBaseRoughness();
Expand Down Expand Up @@ -523,6 +520,26 @@ open3d::geometry::TriangleMesh TriangleMesh::ToLegacy() const {
return mesh_legacy;
}

std::unordered_map<std::string, geometry::TriangleMesh>
TriangleMesh::FromTriangleMeshModel(
const open3d::visualization::rendering::TriangleMeshModel &model,
core::Dtype float_dtype,
core::Dtype int_dtype,
const core::Device &device) {
std::unordered_map<std::string, TriangleMesh> tmeshes;
for (const auto &mobj : model.meshes_) {
auto tmesh = TriangleMesh::FromLegacy(*mobj.mesh, float_dtype,
int_dtype, device);
// material textures will be on the CPU. GPU resident texture images is
// not yet supported. See comment in Material.cpp
tmesh.SetMaterial(
visualization::rendering::Material::FromMaterialRecord(
model.materials_[mobj.material_idx]));
tmeshes.emplace(mobj.mesh_name, tmesh);
}
return tmeshes;
}

TriangleMesh TriangleMesh::To(const core::Device &device, bool copy) const {
if (!copy && GetDevice() == device) {
return *this;
Expand Down
27 changes: 26 additions & 1 deletion cpp/open3d/t/geometry/TriangleMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#pragma once

#include <list>
#include <unordered_map>

#include "open3d/core/Tensor.h"
#include "open3d/core/TensorCheck.h"
Expand All @@ -16,6 +17,7 @@
#include "open3d/t/geometry/DrawableGeometry.h"
#include "open3d/t/geometry/Geometry.h"
#include "open3d/t/geometry/TensorMap.h"
#include "open3d/visualization/rendering/Model.h"

namespace open3d {
namespace t {
Expand Down Expand Up @@ -701,7 +703,8 @@ class TriangleMesh : public Geometry, public DrawableGeometry {
/// values, e.g. vertices, normals, colors.
/// \param int_dtype Int32 or Int64, used to store index values, e.g.
/// triangles.
/// \param device The device where the resulting TriangleMesh resides in.
/// \param device The device where the resulting TriangleMesh resides in
/// (default CPU:0).
static geometry::TriangleMesh FromLegacy(
const open3d::geometry::TriangleMesh &mesh_legacy,
core::Dtype float_dtype = core::Float32,
Expand All @@ -711,6 +714,28 @@ class TriangleMesh : public Geometry, public DrawableGeometry {
/// Convert to a legacy Open3D TriangleMesh.
open3d::geometry::TriangleMesh ToLegacy() const;

/// Convert a TriangleMeshModel (e.g. as read from a file with
/// open3d::io::ReadTriangleMeshModel) to an unordered map of mesh names to
/// TriangleMeshes. Only one material is supported per mesh. Materials
/// common to multiple meshes will be dupicated. Textures (as
/// t::geometry::Image) will use shared storage.
/// \param model TriangleMeshModel to convert.
/// \param float_dtype Float32 or Float64, used to store floating point
/// values, e.g. vertices, normals, colors.
/// \param int_dtype Int32 or Int64, used to store index values, e.g.
/// triangles.
/// \param device The device where the resulting TriangleMesh resides in
/// (default CPU:0). Material textures use CPU storage - GPU resident
/// texture images are not yet supported.
/// \return unordered map of constituent mesh names to TriangleMeshes, with
/// materials.
static std::unordered_map<std::string, geometry::TriangleMesh>
FromTriangleMeshModel(
const open3d::visualization::rendering::TriangleMeshModel &model,
core::Dtype float_dtype = core::Float32,
core::Dtype int_dtype = core::Int64,
const core::Device &device = core::Device("CPU:0"));

/// Compute the convex hull of the triangle mesh using qhull.
///
/// This runs on the CPU.
Expand Down
10 changes: 8 additions & 2 deletions cpp/open3d/t/io/file_format/FileASSIMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// ----------------------------------------------------------------------------

#include <assimp/GltfMaterial.h>
#include <assimp/material.h>
#include <assimp/postprocess.h>
#include <assimp/scene.h>

Expand Down Expand Up @@ -357,12 +358,17 @@ bool WriteTriangleMeshUsingASSIMP(const std::string& filename,
auto r = mesh.GetMaterial().GetBaseClearcoatRoughness();
ai_mat->AddProperty(&r, 1, AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR);
}
if (mesh.GetMaterial().HasEmissiveColor()) {
auto c = mesh.GetMaterial().GetEmissiveColor();
auto ac = aiColor4D(c.x(), c.y(), c.z(), c.w());
ai_mat->AddProperty(&ac, 1, AI_MATKEY_COLOR_EMISSIVE);
}

// 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.
// metal maps are available 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;
Expand Down
78 changes: 78 additions & 0 deletions cpp/open3d/visualization/rendering/Material.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ void Material::SetDefaultProperties() {
SetTransmission(1.f);
SetAbsorptionColor(Eigen::Vector4f(1.f, 1.f, 1.f, 1.f));
SetAbsorptionDistance(1.f);
SetEmissiveColor(Eigen::Vector4f(1.f, 1.f, 1.f, 1.f));
SetPointSize(3.f);
SetLineWidth(1.f);
}
Expand All @@ -39,6 +40,24 @@ void Material::SetTextureMap(const std::string &key,
texture_maps_[key] = image.To(core::Device("CPU:0"), true);
}

std::string Material::ToString() const {
if (!IsValid()) {
return "Invalid Material\n";
}
std::ostringstream os;
os << "Material " << material_name_ << '\n';
for (const auto &kv : scalar_properties_) {
os << '\t' << kv.first << ": " << kv.second << '\n';
}
for (const auto &kv : vector_properties_) {
os << '\t' << kv.first << ": " << kv.second.transpose() << '\n';
}
for (const auto &kv : texture_maps_) {
os << '\t' << kv.first << ": " << kv.second.ToString() << '\n';
}
return os.str();
}

void Material::ToMaterialRecord(MaterialRecord &record) const {
record.shader = GetMaterialName();
// Convert base material properties
Expand All @@ -63,6 +82,9 @@ void Material::ToMaterialRecord(MaterialRecord &record) const {
if (HasAnisotropy()) {
record.base_anisotropy = GetAnisotropy();
}
if (HasEmissiveColor()) {
record.emissive_color = GetEmissiveColor();
}
if (HasThickness()) {
record.thickness = GetThickness();
}
Expand Down Expand Up @@ -124,6 +146,62 @@ void Material::ToMaterialRecord(MaterialRecord &record) const {
}
}

Material Material::FromMaterialRecord(const MaterialRecord &record) {
using t::geometry::Image;
Material tmat(record.shader);
// scalar and vector properties
tmat.SetBaseColor(record.base_color);
tmat.SetBaseMetallic(record.base_metallic);
tmat.SetBaseRoughness(record.base_roughness);
tmat.SetBaseReflectance(record.base_reflectance);
tmat.SetBaseClearcoat(record.base_clearcoat);
tmat.SetBaseClearcoatRoughness(record.base_clearcoat_roughness);
tmat.SetAnisotropy(record.base_anisotropy);
tmat.SetEmissiveColor(record.emissive_color);
// refractive materials
tmat.SetThickness(record.thickness);
tmat.SetTransmission(record.transmission);
tmat.SetAbsorptionDistance(record.absorption_distance);
// points and lines
tmat.SetPointSize(record.point_size);
tmat.SetLineWidth(record.line_width);
// maps
if (record.albedo_img) {
tmat.SetAlbedoMap(Image::FromLegacy(*record.albedo_img));
}
if (record.normal_img) {
tmat.SetNormalMap(Image::FromLegacy(*record.normal_img));
}
if (record.ao_img) {
tmat.SetAOMap(Image::FromLegacy(*record.ao_img));
}
if (record.metallic_img) {
tmat.SetMetallicMap(Image::FromLegacy(*record.metallic_img));
}
if (record.roughness_img) {
tmat.SetRoughnessMap(Image::FromLegacy(*record.roughness_img));
}
if (record.reflectance_img) {
tmat.SetReflectanceMap(Image::FromLegacy(*record.reflectance_img));
}
if (record.clearcoat_img) {
tmat.SetClearcoatMap(Image::FromLegacy(*record.clearcoat_img));
}
if (record.clearcoat_roughness_img) {
tmat.SetClearcoatRoughnessMap(
Image::FromLegacy(*record.clearcoat_roughness_img));
}
if (record.anisotropy_img) {
tmat.SetAnisotropyMap(Image::FromLegacy(*record.anisotropy_img));
}
if (record.ao_rough_metal_img) {
tmat.SetAORoughnessMetalMap(
Image::FromLegacy(*record.ao_rough_metal_img));
}

return tmat;
}

} // namespace rendering
} // namespace visualization
} // namespace open3d
16 changes: 16 additions & 0 deletions cpp/open3d/visualization/rendering/Material.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#pragma once

#include <sstream>
#include <string>

#include "open3d/t/geometry/Image.h"
Expand Down Expand Up @@ -34,6 +35,9 @@ class Material {

Material(const Material &mat) = default;

/// Convert from MaterialRecord
static Material FromMaterialRecord(const MaterialRecord &mat);

Material &operator=(const Material &other) = default;

/// Create an empty but valid material for the specified material name
Expand All @@ -51,6 +55,9 @@ class Material {
/// Get the name of the material.
const std::string &GetMaterialName() const { return material_name_; }

/// String reprentation for printing.
std::string ToString() const;

/// Returns the texture map map
const TextureMaps &GetTextureMaps() const { return texture_maps_; }

Expand Down Expand Up @@ -249,6 +256,9 @@ class Material {
float GetAbsorptionDistance() const {
return GetScalarProperty("absorption_distance");
}
Eigen::Vector4f GetEmissiveColor() const {
return GetVectorProperty("emissive_color");
}

bool HasBaseColor() const { return HasVectorProperty("base_color"); }
bool HasBaseMetallic() const { return HasScalarProperty("metallic"); }
Expand All @@ -267,6 +277,9 @@ class Material {
bool HasAbsorptionDistance() const {
return HasScalarProperty("absorption_distance");
}
bool HasEmissiveColor() const {
return HasVectorProperty("emissive_color");
}

void SetBaseColor(const Eigen::Vector4f &value) {
SetVectorProperty("base_color", value);
Expand Down Expand Up @@ -295,6 +308,9 @@ class Material {
void SetAbsorptionDistance(float value) {
SetScalarProperty("absorption_distance", value);
}
void SetEmissiveColor(const Eigen::Vector4f &value) {
SetVectorProperty("emissive_color", value);
}

////////////////////////////////////////////////////////////////////////////
///
Expand Down
22 changes: 22 additions & 0 deletions cpp/pybind/t/geometry/trianglemesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,28 @@ The attributes of the triangle mesh have different levels::
"vertex_dtype"_a = core::Float32, "triangle_dtype"_a = core::Int64,
"device"_a = core::Device("CPU:0"),
"Create a TriangleMesh from a legacy Open3D TriangleMesh.");
triangle_mesh.def_static(
"from_triangle_mesh_model", &TriangleMesh::FromTriangleMeshModel,
"model"_a, "vertex_dtype"_a = core::Float32,
"triangle_dtype"_a = core::Int64,
"device"_a = core::Device("CPU:0"),
R"(Convert a TriangleMeshModel (e.g. as read from a file with
`open3d.io.read_triangle_mesh_model()`) to a dictionary of mesh names to
triangle meshes with the specified vertex and triangle dtypes and moved to the
specified device. Only a single material per mesh is supported. Materials common
to multiple meshes will be duplicated. Textures (as t.geometry.Image) will use
shared storage on the CPU (GPU resident images for textures is not yet supported).
Returns:
Dictionary of names to triangle meshes.
Example:
flight_helmet = o3d.data.FlightHelmetModel()
model = o3d.io.read_triangle_model(flight_helmet.path)
mesh_dict = o3d.t.geometry.TriangleMesh.from_triangle_mesh_model(model)
o3d.visualization.draw(list({"name": name, "geometry": tmesh} for
(name, tmesh) in mesh_dict.items()))
)");
// conversion
triangle_mesh.def("to_legacy", &TriangleMesh::ToLegacy,
"Convert to a legacy Open3D TriangleMesh.");
Expand Down
4 changes: 4 additions & 0 deletions cpp/pybind/visualization/rendering/material.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "open3d/visualization/rendering/Material.h"

#include "open3d/visualization/rendering/MaterialRecord.h"
#include "pybind/open3d_pybind.h"

PYBIND11_MAKE_OPAQUE(
Expand Down Expand Up @@ -41,6 +42,9 @@ void pybind_material(py::module& m) {
mat.def(py::init<>())
.def(py::init<Material>(), "", "mat"_a)
.def(py::init<const std::string&>(), "", "material_name"_a)
.def(py::init(&Material::FromMaterialRecord), "material_record"_a,
"Convert from MaterialRecord.")
.def("__repr__", &Material::ToString)
.def("set_default_properties", &Material::SetDefaultProperties,
"Fills material with defaults for common PBR material "
"properties used by Open3D")
Expand Down
1 change: 1 addition & 0 deletions cpp/tests/t/geometry/TriangleMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <gmock/gmock.h>

#include "core/CoreTest.h"
#include "open3d/core/Dtype.h"
#include "open3d/core/EigenConverter.h"
#include "open3d/core/TensorCheck.h"
#include "tests/Tests.h"
Expand Down

0 comments on commit 4ae5390

Please sign in to comment.