MEPP2 Project
ObjFileWriter.h
Go to the documentation of this file.
1 // Copyright (c) 2012-2019 University of Lyon and CNRS (France).
2 // All rights reserved.
3 //
4 // This file is part of MEPP2; you can redistribute it and/or modify
5 // it under the terms of the GNU Lesser General Public License as
6 // published by the Free Software Foundation; either version 3 of
7 // the License, or (at your option) any later version.
8 //
9 // This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
10 // WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
11 #pragma once
12 
13 #include <iostream>
14 #include <vector>
15 #include <cstdio>
16 #include <string>
17 #include <exception>
18 
19 #include <boost/filesystem.hpp>
20 
23 #include "FEVV/Types/Material.h"
24 
25 namespace FEVV {
26 namespace IO {
27 
28 using namespace StrUtils;
29 using namespace FileUtils;
30 
34 template< typename MaterialType >
35 void
36 write_mtl_file(const std::string &mtl_file_name,
37  std::vector< MaterialType > &materials)
38 {
39  // create MTL file
40  std::ofstream mtl_file(mtl_file_name, std::ios::out | std::ios::trunc);
41  if(!mtl_file.is_open())
42  throw std::runtime_error("write_MTL_file(): failed to create MTL file.");
43 
44  std::string parent_directory = get_parent_directory(mtl_file_name);
45  if(parent_directory.empty())
46  parent_directory = ".";
47  const std::string texture_filepath_base = parent_directory + '/';
48 
49  // populate MTL file
50  mtl_file << "# MTL File produced by MEPP2" << std::endl;
51  for(auto &material : materials)
52  {
53  mtl_file << "newmtl " << material.name << std::endl;
54 
55  // Writing material coefficients
56  mtl_file << " Kd " << material.diffuse_red_component << ' '
57  << material.diffuse_green_component << ' '
58  << material.diffuse_blue_component << std::endl;
59 
60  if(material.type == Types::MaterialType::MATERIAL_TYPE_PBR)
61  {
62  mtl_file << " Pm " << material.metallic_factor << std::endl;
63  mtl_file << " Pr " << material.roughness_factor << std::endl;
64  }
65  else
66  {
67  mtl_file << " Ka " << material.ambient_red_component << ' '
68  << material.ambient_green_component << ' '
69  << material.ambient_blue_component << std::endl;
70  mtl_file << " Ks " << material.specular_red_component << ' '
71  << material.specular_green_component << ' '
72  << material.specular_blue_component << std::endl;
73  mtl_file << " Ke " << material.emissive_red_component << ' '
74  << material.emissive_green_component << ' '
75  << material.emissive_blue_component << std::endl;
76  mtl_file << " d " << material.transparency << std::endl;
77  }
78 
79  // Diffuse/albedo map
80  if(!material.diffuse_texture_filename.empty())
81  {
82  const std::string texture_filename =
83  get_file_full_name(material.diffuse_texture_filename);
84  mtl_file << " map_Kd " << texture_filename << std::endl;
85 
86  // write texture image to file
87  const auto &img =
88  *(material.images.at(material.diffuse_texture_filename));
89  img.save((texture_filepath_base + texture_filename).c_str());
90  }
91 
92  // Ambient/ambient occlusion map
93  if(!material.ambient_texture_filename.empty())
94  {
95  const std::string texture_filename =
96  get_file_full_name(material.ambient_texture_filename);
97  mtl_file << " map_Ka " << texture_filename << std::endl;
98 
99  // write texture image to file
100  const auto &img =
101  *(material.images.at(material.ambient_texture_filename));
102  img.save((texture_filepath_base + texture_filename).c_str());
103  }
104 
105  // Normal/bump map
106  if(!material.normal_map_filename.empty())
107  {
108  const std::string texture_filename =
109  get_file_full_name(material.normal_map_filename);
110  mtl_file << " "
111  << (material.type == Types::MaterialType::MATERIAL_TYPE_PBR
112  ? "norm"
113  : "bump")
114  << ' ';
115  mtl_file << texture_filename << std::endl;
116 
117  // write texture image to file
118  const auto &img =
119  *(material.images.at(material.normal_map_filename));
120  img.save((texture_filepath_base + texture_filename).c_str());
121  }
122 
123  if(material.type == Types::MaterialType::MATERIAL_TYPE_PBR)
124  {
125  // Metallic map
126  if(!material.metallic_map_filename.empty())
127  {
128  const std::string texture_filename =
129  get_file_full_name(material.metallic_map_filename);
130  mtl_file << " map_Pm " << texture_filename << std::endl;
131 
132  // write texture image to file
133  const auto &img =
134  *(material.images.at(material.metallic_map_filename));
135  img.save((texture_filepath_base + texture_filename).c_str());
136  }
137 
138  // Roughness map
139  if(!material.roughness_map_filename.empty())
140  {
141  const std::string texture_filename =
142  get_file_full_name(material.roughness_map_filename);
143  mtl_file << " map_Pr " << texture_filename << std::endl;
144 
145  // write texture image to file
146  const auto &img =
147  *(material.images.at(material.roughness_map_filename));
148  img.save((texture_filepath_base + texture_filename).c_str());
149  }
150  }
151  else
152  {
153  // Specular map
154  if(!material.specular_texture_filename.empty())
155  {
156  const std::string texture_filename =
157  get_file_full_name(material.specular_texture_filename);
158  mtl_file << " map_Ks " << texture_filename << std::endl;
159 
160  // write texture image to file
161  const auto &img =
162  *(material.images.at(material.specular_texture_filename));
163  img.save((texture_filepath_base + texture_filename).c_str());
164  }
165 
166  // Transparency map
167  if(!material.transparency_texture_filename.empty())
168  {
169  const std::string texture_filename =
170  get_file_full_name(material.transparency_texture_filename);
171  mtl_file << " map_d " << texture_filename << std::endl;
172 
173  // write texture image to file
174  const auto &img =
175  *(material.images.at(material.transparency_texture_filename));
176  img.save((texture_filepath_base + texture_filename).c_str());
177  }
178  }
179 
180  mtl_file << std::endl;
181  }
182 
183  mtl_file.close();
184 }
185 
189 template< typename CoordType,
190  typename CoordNType,
191  typename CoordTType,
192  typename CoordCType,
193  typename IndexType,
194  typename MaterialType >
195 void
196 write_obj_file(const std::string &file_path,
197  std::vector< std::vector< CoordType > > &points_coords,
198  std::vector< std::vector< CoordNType > > &normals_coords,
199  std::vector< std::vector< CoordTType > > &texture_coords,
200  std::vector< std::vector< CoordCType > > &vertex_color_coords,
201  std::vector< std::vector< IndexType > > &face_indices,
202  std::vector< std::vector< IndexType > > &texture_face_indices,
203  std::vector< std::vector< IndexType > > &normal_face_indices,
204  std::vector< MaterialType > &materials, // list of materials
205  std::vector< IndexType > &face_material) // material of each face
206 {
207  size_t nb_points = points_coords.size(), nb_normals = normals_coords.size(),
208  nb_tex_coords = texture_coords.size(),
209  nb_point_color = vertex_color_coords.size(),
210  nb_faces = face_indices.size(),
211  nb_texture_ind = texture_face_indices.size(),
212  nb_normal_ind = normal_face_indices.size(),
213  nb_face_material = face_material.size(),
214  nb_materials = materials.size();
215 
216  bool use_normal = (nb_normals != 0), use_texture = (nb_tex_coords != 0),
217  use_color = (nb_point_color != 0),
218  use_face_material = (nb_face_material != 0),
219  use_materials = (nb_materials != 0);
220 
221  if((nb_point_color != nb_points) && (nb_point_color != 0))
222  throw std::invalid_argument(
223  "Writer::write_obj_file -> invalid number of vertex colors.");
224 
225  if((nb_texture_ind != nb_faces) && (nb_texture_ind != 0))
226  throw std::invalid_argument(
227  "Writer::write_obj_file -> invalid number of texture indices.");
228 
229  if((nb_normal_ind != nb_faces) && (nb_normal_ind != 0))
230  throw std::invalid_argument(
231  "Writer::write_obj_file -> invalid number of normal indices.");
232 
233  if((nb_face_material != nb_faces) && (nb_face_material != 0))
234  throw std::invalid_argument(
235  "Writer::write_obj_file -> invalid number of face materials.");
236 
237  // WRITE MATERIALS TO MTL FILE
238 
239  std::string mtl_file_name;
240 
241  if(use_materials)
242  {
243  std::string parent_directory = get_parent_directory(file_path);
244  if(parent_directory.empty())
245  parent_directory = ".";
246  mtl_file_name = get_file_name(file_path) + ".mtl";
247  std::string mtl_file_path = parent_directory + "/" + mtl_file_name;
248  write_mtl_file(mtl_file_path, materials);
249  }
250 
251  // WRITE MESH TO OBJ FILE
252 
253  std::ofstream file(file_path, std::ios::out | std::ios::trunc);
254 
255  if(file.is_open())
256  {
257  file << "# OBJ File produced by MEPP2" << std::endl
258  << "# The M2Disco Framework" << std::endl
259  << "#" << std::endl;
260  file << "# Mesh with :" << std::endl;
261  file << "# \t" << nb_points << " vertex positions" << std::endl;
262  file << "# \t" << nb_faces << " faces" << std::endl;
263  file << "# \t" << nb_tex_coords << " UV coordinates" << std::endl;
264  file << "# \t" << nb_normals << " vertex normals" << std::endl << std::endl;
265 
266  // WRITE MTL FILENAME
267  if(use_materials)
268  {
269  // store MTL file name in OBJ file
270  file << "mtllib " << mtl_file_name << std::endl;
271  }
272 
273  // VERTICES WRITING
274  typename std::vector< std::vector< CoordCType > >::const_iterator it_c =
275  vertex_color_coords.cbegin();
276  for(typename std::vector< std::vector< CoordType > >::const_iterator it_p =
277  points_coords.cbegin();
278  it_p != points_coords.cend();
279  ++it_p)
280  {
281  file << "v ";
282  for(typename std::vector< CoordType >::const_iterator it_pc =
283  it_p->cbegin();
284  it_pc != it_p->cend();
285  ++it_pc)
286  file << *it_pc << " ";
287 
288  if(use_color)
289  {
290  for(typename std::vector< CoordCType >::const_iterator it_cc =
291  it_c->cbegin();
292  it_cc != it_c->cend();
293  ++it_cc)
294  file << *it_cc << " ";
295  ++it_c;
296  }
297 
298  file << std::endl;
299  }
300 
301  file << std::endl;
302 
303  // NORMALS WRITING
304  for(typename std::vector< std::vector< CoordNType > >::const_iterator it_n =
305  normals_coords.cbegin();
306  it_n != normals_coords.cend();
307  ++it_n)
308  {
309  file << "vn ";
310  for(typename std::vector< CoordNType >::const_iterator it_nc =
311  it_n->cbegin();
312  it_nc != it_n->cend();
313  ++it_nc)
314  file << *it_nc << " ";
315  file << std::endl;
316  }
317 
318  // TEXTURE COORDS WRITING
319  for(typename std::vector< std::vector< CoordTType > >::const_iterator it_t =
320  texture_coords.cbegin();
321  it_t != texture_coords.cend();
322  ++it_t)
323  {
324  file << "vt ";
325  for(typename std::vector< CoordTType >::const_iterator it_tc =
326  it_t->cbegin();
327  it_tc != it_t->end();
328  ++it_tc)
329  file << *it_tc << " ";
330  file << std::endl;
331  }
332 
333  // FACES WRITING
334  IndexType current_material_id = -1;
335  std::string current_material_name;
336  for(unsigned int no_face = 0; no_face < nb_faces; ++no_face)
337  {
338  // write a 'usemtl' line when material changes
339  if(use_face_material && face_material[no_face] != current_material_id)
340  {
341  current_material_id = face_material[no_face];
342  file << "usemtl " << materials[current_material_id].name << std::endl;
343  }
344 
345  // write face line
346  file << "f ";
347  for(unsigned int no_vertex = 0; no_vertex < face_indices[no_face].size();
348  ++no_vertex)
349  {
350  file << (face_indices[no_face][no_vertex] +
351  1); // +1 because obj format starts the indices to 1 (not 0)
352 
353  if(use_texture || use_normal)
354  file << "/";
355 
356  if(use_texture)
357  file << (texture_face_indices[no_face][no_vertex] + 1);
358 
359  if(use_normal)
360  file << "/" << (normal_face_indices[no_face][no_vertex] + 1);
361 
362  file << " ";
363  }
364  file << std::endl;
365  }
366 
367  file.close();
368  }
369  else
370  {
371  throw std::runtime_error(
372  "Writer::write_obj_file -> output file failed to open.");
373  }
374 }
375 
376 } // namespace IO
377 } // namespace FEVV
378 
FEVV::Types::MaterialType
MaterialType
Definition: Material.h:38
FEVV::IO::write_obj_file
void write_obj_file(const std::string &file_path, std::vector< std::vector< CoordType > > &points_coords, std::vector< std::vector< CoordNType > > &normals_coords, std::vector< std::vector< CoordTType > > &texture_coords, std::vector< std::vector< CoordCType > > &vertex_color_coords, std::vector< std::vector< IndexType > > &face_indices, std::vector< std::vector< IndexType > > &texture_face_indices, std::vector< std::vector< IndexType > > &normal_face_indices, std::vector< MaterialType > &materials, std::vector< IndexType > &face_material)
Definition: ObjFileWriter.h:196
Material.h
FEVV::FileUtils::get_file_name
std::string get_file_name(const std::string &file_name)
Definition: FileUtilities.hpp:128
FEVV::FileUtils::get_file_full_name
std::string get_file_full_name(const std::string &file_name)
Definition: FileUtilities.hpp:152
FEVV::Types::MaterialType::MATERIAL_TYPE_STANDARD
@ MATERIAL_TYPE_STANDARD
FEVV
Interfaces for plugins These interfaces will be used for different plugins.
Definition: Assert.h:16
FEVV::face_material
@ face_material
Definition: properties.h:85
StringUtilities.hpp
FEVV::FileUtils::get_parent_directory
std::string get_parent_directory(const std::string &file_name)
Definition: FileUtilities.hpp:191
FEVV::IO::write_mtl_file
void write_mtl_file(const std::string &mtl_file_name, std::vector< MaterialType > &materials)
Definition: ObjFileWriter.h:36
FileUtilities.hpp