eos 1.4.0
Loading...
Searching...
No Matches
write_obj.hpp
1/*
2 * eos - A 3D Morphable Model fitting library written in modern C++11/14.
3 *
4 * File: include/eos/core/write_obj.hpp
5 *
6 * Copyright 2017-2020 Patrik Huber
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20#pragma once
21
22#ifndef EOS_WRITE_OBJ_HPP
23#define EOS_WRITE_OBJ_HPP
24
25#include "eos/core/Mesh.hpp"
26
27#include <cassert>
28#include <fstream>
29#include <string>
30#include <stdexcept>
31
32namespace eos {
33namespace core {
34
43inline void write_obj(const Mesh& mesh, std::string filename)
44{
45 assert(mesh.vertices.size() == mesh.colors.size() || mesh.colors.empty());
46 assert(mesh.tvi.size() == mesh.tti.size() || mesh.tti.empty());
47
48 std::ofstream obj_file(filename);
49
50 if (mesh.colors.empty())
51 {
52 for (std::size_t i = 0; i < mesh.vertices.size(); ++i)
53 {
54 obj_file << "v " << mesh.vertices[i][0] << " " << mesh.vertices[i][1] << " "
55 << mesh.vertices[i][2] << std::endl;
56 }
57 } else
58 {
59 for (std::size_t i = 0; i < mesh.vertices.size(); ++i)
60 {
61 obj_file << "v " << mesh.vertices[i][0] << " " << mesh.vertices[i][1] << " "
62 << mesh.vertices[i][2] << " " << mesh.colors[i][0] << " " << mesh.colors[i][1] << " "
63 << mesh.colors[i][2] << std::endl;
64 }
65 }
66
67 if (!mesh.texcoords.empty())
68 {
69 for (auto&& tc : mesh.texcoords)
70 {
71 obj_file << "vt " << tc[0] << " " << 1.0f - tc[1] << std::endl;
72 // We invert y because MeshLab's uv origin (0, 0) is on the bottom-left
73 }
74 }
75
76 for (std::size_t i = 0; i < mesh.tvi.size(); ++i)
77 {
78 const auto& vi = mesh.tvi[i];
79 // We add one to the indices because obj starts counting triangle indices at 1
80 if (mesh.texcoords.empty())
81 {
82 obj_file << "f " << vi[0] + 1 << " " << vi[1] + 1 << " " << vi[2] + 1 << std::endl;
83 } else
84 {
85 if (mesh.tti.empty())
86 {
87 assert(mesh.texcoords.size() == mesh.vertices.size());
88 // Using the mesh triangulation as uv triangle indices:
89 obj_file << "f " << vi[0] + 1 << "/" << vi[0] + 1 << " " << vi[1] + 1 << "/" << vi[1] + 1
90 << " " << vi[2] + 1 << "/" << vi[2] + 1 << std::endl;
91 } else
92 {
93 // Separate triangle indices for the uv coordinates:
94 const auto& ti = mesh.tti[i];
95 obj_file << "f " << vi[0] + 1 << "/" << ti[0] + 1 << " " << vi[1] + 1 << "/" << ti[1] + 1
96 << " " << vi[2] + 1 << "/" << ti[2] + 1 << std::endl;
97 }
98 }
99 }
100
101 return;
102}
103
115inline void write_textured_obj(const Mesh& mesh, std::string filename)
116{
117 assert(mesh.vertices.size() == mesh.colors.size() || mesh.colors.empty());
118 assert(!mesh.texcoords.empty());
119 assert(mesh.tvi.size() == mesh.tti.size() || mesh.tti.empty());
120
121 if (filename.at(filename.size() - 4) != '.')
122 {
123 throw std::runtime_error(
124 "Error in given filename: Expected a dot and a 3-letter extension at the end (i.e. '.obj'). " +
125 filename);
126 }
127
128 // Takes a full path to a file and returns only the filename:
129 const auto get_filename = [](const std::string& path) {
130 auto last_slash = path.find_last_of("/\\");
131 if (last_slash == std::string::npos)
132 {
133 return path;
134 }
135 return path.substr(last_slash + 1, path.size());
136 };
137
138 std::ofstream obj_file(filename);
139
140 std::string mtl_filename(filename);
141 // replace '.obj' at the end with '.mtl':
142 mtl_filename.replace(std::end(mtl_filename) - 4, std::end(mtl_filename), ".mtl");
143
144 obj_file << "mtllib " << get_filename(mtl_filename) << std::endl; // first line of the obj file
145
146 // same as in write_obj():
147 if (mesh.colors.empty())
148 {
149 for (std::size_t i = 0; i < mesh.vertices.size(); ++i)
150 {
151 obj_file << "v " << mesh.vertices[i][0] << " " << mesh.vertices[i][1] << " "
152 << mesh.vertices[i][2] << " " << std::endl;
153 }
154 } else
155 {
156 for (std::size_t i = 0; i < mesh.vertices.size(); ++i)
157 {
158 obj_file << "v " << mesh.vertices[i][0] << " " << mesh.vertices[i][1] << " "
159 << mesh.vertices[i][2] << " " << mesh.colors[i][0] << " " << mesh.colors[i][1] << " "
160 << mesh.colors[i][2] << " " << std::endl;
161 }
162 }
163 // end
164
165 for (std::size_t i = 0; i < mesh.texcoords.size(); ++i)
166 {
167 obj_file << "vt " << mesh.texcoords[i][0] << " " << 1.0f - mesh.texcoords[i][1] << std::endl;
168 // We invert y because MeshLab's uv origin (0, 0) is on the bottom-left
169 }
170
171 // Note: This can't be at the top, otherwise MeshLab displays the mesh in all-black.
172 obj_file << "usemtl FaceTexture" << std::endl; // the name of our texture (material) will be 'FaceTexture'
173
174 for (std::size_t i = 0; i < mesh.tvi.size(); ++i)
175 {
176 const auto& vi = mesh.tvi[i];
177 // We add one to the indices because obj starts counting triangle indices at 1
178 if (mesh.tti.empty())
179 {
180 assert(mesh.texcoords.size() == mesh.vertices.size());
181 // Using the mesh triangulation as uv triangle indices:
182 obj_file << "f " << vi[0] + 1 << "/" << vi[0] + 1 << " " << vi[1] + 1 << "/" << vi[1] + 1 << " "
183 << vi[2] + 1 << "/" << vi[2] + 1 << std::endl;
184 } else
185 {
186 // Separate triangle indices for the uv coordinates:
187 const auto& ti = mesh.tti[i];
188 obj_file << "f " << vi[0] + 1 << "/" << ti[0] + 1 << " " << vi[1] + 1 << "/" << ti[1] + 1 << " "
189 << vi[2] + 1 << "/" << ti[2] + 1 << std::endl;
190 }
191 }
192
193 std::ofstream mtl_file(mtl_filename);
194 std::string texture_filename(filename);
195 // replace '.obj' at the end with '.texture.png':
196 texture_filename.replace(std::end(texture_filename) - 4, std::end(texture_filename), ".texture.png");
197
198 mtl_file << "newmtl FaceTexture" << std::endl;
199 mtl_file << "map_Kd " << get_filename(texture_filename) << std::endl;
200
201 return;
202};
203
204} /* namespace core */
205} /* namespace eos */
206
207#endif /* EOS_WRITE_OBJ_HPP */
void write_obj(const Mesh &mesh, std::string filename)
Writes the given Mesh to an obj file that for example can be read by MeshLab.
Definition: write_obj.hpp:43
void write_textured_obj(const Mesh &mesh, std::string filename)
Writes an obj file of the given Mesh, including texture coordinates, and an mtl file containing a ref...
Definition: write_obj.hpp:115
Namespace containing all of eos's 3D model fitting functionality.
This class represents a 3D mesh consisting of vertices, vertex colour information and texture coordin...
Definition: Mesh.hpp:45
std::vector< Eigen::Vector3f > colors
Colour information for each vertex. Expected to be in RGB order.
Definition: Mesh.hpp:47
std::vector< Eigen::Vector3f > vertices
3D vertex positions.
Definition: Mesh.hpp:46
std::vector< std::array< int, 3 > > tvi
Triangle vertex indices.
Definition: Mesh.hpp:50
std::vector< std::array< int, 3 > > tti
Triangle texture indices.
Definition: Mesh.hpp:52
std::vector< Eigen::Vector2f > texcoords
Texture coordinates.
Definition: Mesh.hpp:48