Skip to content

Commit

Permalink
Support loading PBR textures in OBJLoader (#216)
Browse files Browse the repository at this point in the history
Extends the OBJLoader to support loading PBR textures. The Wavefront OBJ format has PBR extensions which our current version of the tiny_obj_loader is already able to parse and load. This updates the loader to load these new fields into the common::Material and common::Pbr data structures.

Signed-off-by: Ian Chen <[email protected]>
  • Loading branch information
iche033 authored May 14, 2021
1 parent 2e24122 commit 7560ac9
Show file tree
Hide file tree
Showing 6 changed files with 13,129 additions and 0 deletions.
59 changes: 59 additions & 0 deletions graphics/src/OBJLoader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,21 @@ Mesh *OBJLoader::Load(const std::string &_filename)
std::map<std::string, Material *> materialIds;
std::string path = common::parentPath(_filename);

// check if obj is exported by blender
// blender shoves BR fields in standard textures
bool exportedByBlender = false;
std::ifstream infile(_filename);
if (infile.good())
{
std::string line;
std::getline(infile, line);
std::transform(line.begin(), line.end(), line.begin(),
[](unsigned char c){ return std::tolower(c); });
if (line.find("blender") != std::string::npos)
exportedByBlender = true;
}
infile.close();

tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
Expand Down Expand Up @@ -131,6 +146,50 @@ Mesh *OBJLoader::Load(const std::string &_filename)
mat->SetTransparency(1.0 - m.dissolve);
if (!m.diffuse_texname.empty())
mat->SetTextureImage(m.diffuse_texname, path.c_str());

// load PBR textures
// Some obj exporters put PBR maps in the standard textures
// while others have proper support for obj PBR extension
Pbr pbrMat;
// PBR shoved into standard textures
if (!m.specular_texname.empty())
pbrMat.SetRoughnessMap(m.specular_texname);

// check if obj is exported by blender
// blender obj exporter puts roughness map in specular highlight
// field and metalness map in reflection map field!
// see summary in https://developer.blender.org/D8868
// detailing the existing exporter issues
// todo(anyone) add a check for blender version to avoid this hack
// when blender fixes their exporter issue
if (!m.specular_highlight_texname.empty() && exportedByBlender)
{
pbrMat.SetRoughnessMap(m.specular_highlight_texname);
if (!m.reflection_texname.empty())
pbrMat.SetMetalnessMap(m.reflection_texname);
}
else if (!m.reflection_texname.empty())
{
pbrMat.SetEnvironmentMap(m.reflection_texname);
}
if (!m.bump_texname.empty())
pbrMat.SetNormalMap(m.bump_texname);

// PBR extension - overrides standard materials
if (!m.roughness_texname.empty())
pbrMat.SetRoughnessMap(m.roughness_texname);
if (!m.metallic_texname.empty())
pbrMat.SetMetalnessMap(m.metallic_texname);
if (!m.normal_texname.empty())
pbrMat.SetNormalMap(m.normal_texname);
if (!m.emissive_texname.empty())
pbrMat.SetEmissiveMap(m.emissive_texname);

pbrMat.SetRoughness(m.roughness);
pbrMat.SetMetalness(m.metallic);

mat->SetPbrMaterial(pbrMat);

materialIds[m.name] = mat;
}
int matIndex = mesh->IndexOfMaterial(mat);
Expand Down
60 changes: 60 additions & 0 deletions graphics/src/OBJLoader_TEST.cc
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,69 @@ TEST_F(OBJLoaderTest, InvalidMaterial)
common::testing::TestFile("data", "invalid_material.obj");

ignition::common::Mesh *mesh = objLoader.Load(meshFilename);

EXPECT_TRUE(mesh != nullptr);
}

/////////////////////////////////////////////////
// This tests opening an OBJ file that has PBR fields
TEST_F(OBJLoaderTest, PBR)
{
ignition::common::OBJLoader objLoader;

// load obj file exported by 3ds max that has pbr extension
{
std::string meshFilename =
common::testing::TestFile("data", "cube_pbr.obj");

ignition::common::Mesh *mesh = objLoader.Load(meshFilename);
EXPECT_NE(nullptr, mesh);

const common::MaterialPtr mat = mesh->MaterialByIndex(0u);
ASSERT_TRUE(mat.get());

EXPECT_EQ(math::Color(0.0f, 0.0f, 0.0f, 1.0f), mat->Ambient());
EXPECT_EQ(math::Color(0.5f, 0.5f, 0.5f, 1.0f), mat->Diffuse());
EXPECT_EQ(math::Color(1.0f, 1.0f, 1.0f, 1.0f), mat->Specular());
EXPECT_DOUBLE_EQ(0.0, mat->Transparency());
EXPECT_NE(std::string::npos,
mat->TextureImage().find("LightDome_Albedo.png"));
const common::Pbr *pbr = mat->PbrMaterial();
EXPECT_DOUBLE_EQ(0, pbr->Roughness());
EXPECT_DOUBLE_EQ(0, pbr->Metalness());
EXPECT_EQ("LightDome_Metalness.png", pbr->MetalnessMap());
EXPECT_EQ("LightDome_Roughness.png", pbr->RoughnessMap());
EXPECT_EQ("LightDome_Normal.png", pbr->NormalMap());
}

// load obj file exported by blender - it shoves pbr maps into
// existing fields
{
std::string meshFilename =
common::testing::TestFile("data", "blender_pbr.obj");

ignition::common::Mesh *mesh = objLoader.Load(meshFilename);
EXPECT_NE(nullptr, mesh);

const common::MaterialPtr mat = mesh->MaterialByIndex(0u);
ASSERT_TRUE(mat.get());

EXPECT_EQ(math::Color(1.0f, 1.0f, 1.0f, 1.0f), mat->Ambient());
EXPECT_EQ(math::Color(0.8f, 0.8f, 0.8f, 1.0f), mat->Diffuse());
EXPECT_EQ(math::Color(0.5f, 0.5f, 0.5f, 1.0f), mat->Specular());
EXPECT_EQ(math::Color(0.0f, 0.0f, 0.0f, 1.0f), mat->Emissive());
EXPECT_DOUBLE_EQ(0.0, mat->Transparency());
EXPECT_NE(std::string::npos,
mat->TextureImage().find("mesh_Diffuse.png"));
const common::Pbr *pbr = mat->PbrMaterial();
EXPECT_DOUBLE_EQ(0, pbr->Roughness());
EXPECT_DOUBLE_EQ(0, pbr->Metalness());
EXPECT_EQ("mesh_Metal.png", pbr->MetalnessMap());
EXPECT_EQ("mesh_Rough.png", pbr->RoughnessMap());
EXPECT_EQ("mesh_Normal.png", pbr->NormalMap());
}
}

/////////////////////////////////////////////////
int main(int argc, char **argv)
{
Expand Down
16 changes: 16 additions & 0 deletions test/data/blender_pbr.mtl
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Blender MTL File: 'WheelchairLightmap.blend'
# Material Count: 1

newmtl WheelchairUnfolded
Ns 225.000000
Ka 1.000000 1.000000 1.000000
Kd 0.800000 0.800000 0.800000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
map_Bump mesh_Normal.png
map_Kd mesh_Diffuse.png
map_Ns mesh_Rough.png
refl mesh_Metal.png
Loading

0 comments on commit 7560ac9

Please sign in to comment.