Skip to content

Commit

Permalink
Moved write_obj functions to separate header file
Browse files Browse the repository at this point in the history
This makes Mesh.hpp much more lightweight to include as it doesn't require <fstream> and <string> anymore.
Users that use write_obj or write_textured_obj will now need to explicitly #include "eos/core/write_obj.hpp".
  • Loading branch information
patrikhuber committed May 13, 2019
1 parent 4381fd1 commit a040c0e
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 137 deletions.
1 change: 1 addition & 0 deletions examples/fit-model-multi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "eos/core/read_pts_landmarks.hpp"
#include "eos/core/Image.hpp"
#include "eos/core/image/opencv_interop.hpp"
#include "eos/core/write_obj.hpp"
#include "eos/morphablemodel/MorphableModel.hpp"
#include "eos/morphablemodel/Blendshape.hpp"
#include "eos/fitting/fitting.hpp"
Expand Down
1 change: 1 addition & 0 deletions examples/fit-model-simple.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "eos/core/Landmark.hpp"
#include "eos/core/LandmarkMapper.hpp"
#include "eos/core/read_pts_landmarks.hpp"
#include "eos/core/write_obj.hpp"
#include "eos/fitting/RenderingParameters.hpp"
#include "eos/fitting/linear_shape_fitting.hpp"
#include "eos/fitting/orthographic_camera_estimation_linear.hpp"
Expand Down
1 change: 1 addition & 0 deletions examples/fit-model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "eos/core/Landmark.hpp"
#include "eos/core/LandmarkMapper.hpp"
#include "eos/core/read_pts_landmarks.hpp"
#include "eos/core/write_obj.hpp"
#include "eos/fitting/fitting.hpp"
#include "eos/morphablemodel/Blendshape.hpp"
#include "eos/morphablemodel/MorphableModel.hpp"
Expand Down
1 change: 1 addition & 0 deletions examples/generate-obj.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
*/
#include "eos/core/Image.hpp"
#include "eos/core/image/opencv_interop.hpp"
#include "eos/core/write_obj.hpp"
#include "eos/morphablemodel/MorphableModel.hpp"
#include "eos/render/render.hpp"
#include "eos/cpp17/optional.hpp"
Expand Down
137 changes: 0 additions & 137 deletions include/eos/core/Mesh.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@
#include "Eigen/Core"

#include <array>
#include <cassert>
#include <fstream>
#include <string>
#include <vector>

namespace eos {
Expand Down Expand Up @@ -55,140 +52,6 @@ struct Mesh
std::vector<std::array<int, 3>> tti; ///< Triangle texture indices
};

/**
* @brief Writes the given Mesh to an obj file that for example can be read by MeshLab.
*
* If the mesh contains vertex colour information, it will be written to the obj as well.
*
* @param[in] mesh The mesh to save as obj.
* @param[in] filename Output filename (including ".obj").
*/
inline void write_obj(Mesh mesh, std::string filename)
{
assert(mesh.vertices.size() == mesh.colors.size() || mesh.colors.empty());

std::ofstream obj_file(filename);

if (mesh.colors.empty())
{
for (std::size_t i = 0; i < mesh.vertices.size(); ++i)
{
obj_file << "v " << mesh.vertices[i][0] << " " << mesh.vertices[i][1] << " "
<< mesh.vertices[i][2] << std::endl;
}
} else
{
for (std::size_t i = 0; i < mesh.vertices.size(); ++i)
{
obj_file << "v " << mesh.vertices[i][0] << " " << mesh.vertices[i][1] << " "
<< mesh.vertices[i][2] << " " << mesh.colors[i][0] << " " << mesh.colors[i][1] << " "
<< mesh.colors[i][2] << std::endl;
}
}

if (!mesh.texcoords.empty())
{
for (auto&& tc : mesh.texcoords)
{
obj_file << "vt " << tc[0] << " " << tc[1] << std::endl;
}
}

for (auto&& v : mesh.tvi)
{
// Add one because obj starts counting triangle indices at 1
obj_file << "f " << v[0] + 1 << " " << v[1] + 1 << " " << v[2] + 1 << std::endl;
}

return;
}

/**
* @brief Writes an obj file of the given Mesh, including texture coordinates,
* and an mtl file containing a reference to the isomap.
*
* The obj will contain texture coordinates for the mesh, and the
* mtl file will link to a file named <filename>.isomap.png.
* Note that the texture (isomap) has to be saved separately.
*
* @param[in] mesh The mesh to save as obj.
* @param[in] filename Output filename, including .obj.
*/
inline void write_textured_obj(Mesh mesh, std::string filename)
{
assert((mesh.vertices.size() == mesh.colors.size() || mesh.colors.empty()) && !mesh.texcoords.empty());

if (filename.at(filename.size() - 4) != '.')
{
throw std::runtime_error(
"Error in given filename: Expected a dot and a 3-letter extension at the end (i.e. '.obj'). " +
filename);
}

// Takes a full path to a file and returns only the filename:
const auto get_filename = [](const std::string& path) {
auto last_slash = path.find_last_of("/\\");
if (last_slash == std::string::npos)
{
return path;
}
return path.substr(last_slash + 1, path.size());
};

std::ofstream obj_file(filename);

std::string mtl_filename(filename);
// replace '.obj' at the end with '.mtl':
mtl_filename.replace(std::end(mtl_filename) - 4, std::end(mtl_filename), ".mtl");

obj_file << "mtllib " << get_filename(mtl_filename) << std::endl; // first line of the obj file

// same as in write_obj():
if (mesh.colors.empty())
{
for (std::size_t i = 0; i < mesh.vertices.size(); ++i)
{
obj_file << "v " << mesh.vertices[i][0] << " " << mesh.vertices[i][1] << " "
<< mesh.vertices[i][2] << " " << std::endl;
}
} else
{
for (std::size_t i = 0; i < mesh.vertices.size(); ++i)
{
obj_file << "v " << mesh.vertices[i][0] << " " << mesh.vertices[i][1] << " "
<< mesh.vertices[i][2] << " " << mesh.colors[i][0] << " " << mesh.colors[i][1] << " "
<< mesh.colors[i][2] << " " << std::endl;
}
}
// end

for (std::size_t i = 0; i < mesh.texcoords.size(); ++i)
{
obj_file << "vt " << mesh.texcoords[i][0] << " " << 1.0f - mesh.texcoords[i][1] << std::endl;
// We invert y because Meshlab's uv origin (0, 0) is on the bottom-left
}

obj_file << "usemtl FaceTexture" << std::endl; // the name of our texture (material) will be 'FaceTexture'

for (auto&& v : mesh.tvi)
{
// This assumes mesh.texcoords.size() == mesh.vertices.size(). The texture indices could theoretically be different (for example in the cube-mapped 3D scan).
// Add one because obj starts counting triangle indices at 1
obj_file << "f " << v[0] + 1 << "/" << v[0] + 1 << " " << v[1] + 1 << "/" << v[1] + 1 << " "
<< v[2] + 1 << "/" << v[2] + 1 << std::endl;
}

std::ofstream mtl_file(mtl_filename);
std::string texture_filename(filename);
// replace '.obj' at the end with '.isomap.png':
texture_filename.replace(std::end(texture_filename) - 4, std::end(texture_filename), ".isomap.png");

mtl_file << "newmtl FaceTexture" << std::endl;
mtl_file << "map_Kd " << get_filename(texture_filename) << std::endl;

return;
};

} /* namespace core */
} /* namespace eos */

Expand Down
172 changes: 172 additions & 0 deletions include/eos/core/write_obj.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*
* eos - A 3D Morphable Model fitting library written in modern C++11/14.
*
* File: include/eos/core/write_obj.hpp
*
* Copyright 2017-2019 Patrik Huber
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once

#ifndef EOS_WRITE_OBJ_HPP
#define EOS_WRITE_OBJ_HPP

#include "eos/core/Mesh.hpp"

#include <cassert>
#include <fstream>
#include <string>
#include <stdexcept>

namespace eos {
namespace core {

/**
* @brief Writes the given Mesh to an obj file that for example can be read by MeshLab.
*
* If the mesh contains vertex colour information, it will be written to the obj as well.
*
* @param[in] mesh The mesh to save as obj.
* @param[in] filename Output filename (including ".obj").
*/
inline void write_obj(Mesh mesh, std::string filename)
{
assert(mesh.vertices.size() == mesh.colors.size() || mesh.colors.empty());

std::ofstream obj_file(filename);

if (mesh.colors.empty())
{
for (std::size_t i = 0; i < mesh.vertices.size(); ++i)
{
obj_file << "v " << mesh.vertices[i][0] << " " << mesh.vertices[i][1] << " "
<< mesh.vertices[i][2] << std::endl;
}
} else
{
for (std::size_t i = 0; i < mesh.vertices.size(); ++i)
{
obj_file << "v " << mesh.vertices[i][0] << " " << mesh.vertices[i][1] << " "
<< mesh.vertices[i][2] << " " << mesh.colors[i][0] << " " << mesh.colors[i][1] << " "
<< mesh.colors[i][2] << std::endl;
}
}

if (!mesh.texcoords.empty())
{
for (auto&& tc : mesh.texcoords)
{
obj_file << "vt " << tc[0] << " " << tc[1] << std::endl;
}
}

for (auto&& v : mesh.tvi)
{
// Add one because obj starts counting triangle indices at 1
obj_file << "f " << v[0] + 1 << " " << v[1] + 1 << " " << v[2] + 1 << std::endl;
}

return;
}

/**
* @brief Writes an obj file of the given Mesh, including texture coordinates,
* and an mtl file containing a reference to the isomap.
*
* The obj will contain texture coordinates for the mesh, and the
* mtl file will link to a file named <filename>.isomap.png.
* Note that the texture (isomap) has to be saved separately.
*
* @param[in] mesh The mesh to save as obj.
* @param[in] filename Output filename, including .obj.
*/
inline void write_textured_obj(Mesh mesh, std::string filename)
{
assert((mesh.vertices.size() == mesh.colors.size() || mesh.colors.empty()) && !mesh.texcoords.empty());

if (filename.at(filename.size() - 4) != '.')
{
throw std::runtime_error(
"Error in given filename: Expected a dot and a 3-letter extension at the end (i.e. '.obj'). " +
filename);
}

// Takes a full path to a file and returns only the filename:
const auto get_filename = [](const std::string& path) {
auto last_slash = path.find_last_of("/\\");
if (last_slash == std::string::npos)
{
return path;
}
return path.substr(last_slash + 1, path.size());
};

std::ofstream obj_file(filename);

std::string mtl_filename(filename);
// replace '.obj' at the end with '.mtl':
mtl_filename.replace(std::end(mtl_filename) - 4, std::end(mtl_filename), ".mtl");

obj_file << "mtllib " << get_filename(mtl_filename) << std::endl; // first line of the obj file

// same as in write_obj():
if (mesh.colors.empty())
{
for (std::size_t i = 0; i < mesh.vertices.size(); ++i)
{
obj_file << "v " << mesh.vertices[i][0] << " " << mesh.vertices[i][1] << " "
<< mesh.vertices[i][2] << " " << std::endl;
}
} else
{
for (std::size_t i = 0; i < mesh.vertices.size(); ++i)
{
obj_file << "v " << mesh.vertices[i][0] << " " << mesh.vertices[i][1] << " "
<< mesh.vertices[i][2] << " " << mesh.colors[i][0] << " " << mesh.colors[i][1] << " "
<< mesh.colors[i][2] << " " << std::endl;
}
}
// end

for (std::size_t i = 0; i < mesh.texcoords.size(); ++i)
{
obj_file << "vt " << mesh.texcoords[i][0] << " " << 1.0f - mesh.texcoords[i][1] << std::endl;
// We invert y because Meshlab's uv origin (0, 0) is on the bottom-left
}

obj_file << "usemtl FaceTexture" << std::endl; // the name of our texture (material) will be 'FaceTexture'

for (auto&& v : mesh.tvi)
{
// This assumes mesh.texcoords.size() == mesh.vertices.size(). The texture indices could theoretically be different (for example in the cube-mapped 3D scan).
// Add one because obj starts counting triangle indices at 1
obj_file << "f " << v[0] + 1 << "/" << v[0] + 1 << " " << v[1] + 1 << "/" << v[1] + 1 << " "
<< v[2] + 1 << "/" << v[2] + 1 << std::endl;
}

std::ofstream mtl_file(mtl_filename);
std::string texture_filename(filename);
// replace '.obj' at the end with '.isomap.png':
texture_filename.replace(std::end(texture_filename) - 4, std::end(texture_filename), ".isomap.png");

mtl_file << "newmtl FaceTexture" << std::endl;
mtl_file << "map_Kd " << get_filename(texture_filename) << std::endl;

return;
};

} /* namespace core */
} /* namespace eos */

#endif /* EOS_WRITE_OBJ_HPP */
1 change: 1 addition & 0 deletions python/generate-python-bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "eos/core/LandmarkMapper.hpp"
#include "eos/core/Mesh.hpp"
#include "eos/core/read_obj.hpp"
#include "eos/core/write_obj.hpp"
#include "eos/core/Image.hpp"
#include "eos/fitting/RenderingParameters.hpp"
#include "eos/fitting/contour_correspondence.hpp"
Expand Down

0 comments on commit a040c0e

Please sign in to comment.