Vis 2 Demo  1.0
Technical illustration type real-time rendering of geometry
 All Classes Namespaces Files Functions Variables Typedefs Macros
LoaderOBJ.cpp
Go to the documentation of this file.
1 #include <fstream>
2 #include <string>
3 #include <sstream>
4 #include "LoaderOBJ.h"
5 
6 using namespace vis2;
7 
8 LoaderOBJ::LoaderOBJ(const std::string & _file_path) : file_path(_file_path), file_type(VIS2_FILE_TYPE_OBJ)
9 {
10  // check file extension
11  if (_file_path.size() > 4 && !_file_path.substr(_file_path.size()-4).compare(".obj")) {
13  }
14  else if (_file_path.size() > 4 && !_file_path.substr(_file_path.size()-4).compare(".dat"))
15  {
17  }
18  else
19  {
20  std::cout << "Valid file extensions are \"obj\" and \"dat\" !" << std::endl;
21  system("PAUSE");
22  exit(-1);
23  }
24 }
25 
27 {
28  if (vert_positions)
29  delete [] vert_positions;
30  if (vert_normals)
31  delete [] vert_normals;
32  if (vert_uvs)
33  delete [] vert_uvs;
34  if (face_ind)
35  delete [] face_ind;
36 }
37 
39 {
40 
41  std::cout << "Loading geometry from file \"" << file_path << "\"." << std::endl;
43  {
46  }
47  else
49 
50  return 0;
51 }
52 
54 // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //
55 // %% ---------------------------- FILE HANDLING --------------------------- %% //
56 // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //
58 
60 {
62  std::FILE *f;
63  fopen_s(&f,file_path.c_str(),"r");
64  if(!f)
65  {
66  std::cout << "Failed to open file \"" << file_path << "\" !" << std::endl;
67  system("PAUSE");
68  exit(-1);
69  }
70 
71  // read and save content
72  parseOBJ(f);
73 
74  // close file
75  if (f)
76  {
77  std::fclose(f);
78  }
79  return 0;
80 }
81 
83 {
84  // open stream
85  std::ifstream binary_file;
86  binary_file.open(this->file_path, std::ofstream::binary | std::ofstream::in);
87  checkForErrors(binary_file);
88 
89  // read
90  binary_file.read(reinterpret_cast< char *>(&single_ind), sizeof(unsigned int)); // vertex nr
91  binary_file.read(reinterpret_cast< char *>(&nr_face_ind), sizeof(unsigned int)); // index nr
92 
93  // allocate enough memory for the arrays
94  vert_positions = new GLfloat[single_ind * 3];
95  vert_normals = new GLfloat[single_ind * 3];
96  vert_uvs = new GLfloat[single_ind * 3];
97  face_ind = new GLuint[nr_face_ind];
98 
99  // read arrays
100  binary_file.read(reinterpret_cast< char *>(vert_positions), single_ind * 3 * sizeof(GLfloat));
101  // std::cout << "test: " << vert_positions[0] << " " << vert_positions[1] << " " << vert_positions[2] << std::endl;
102  // std::cout << "vert_positions pos: " << binary_file.tellg() << std::endl;
103  binary_file.read(reinterpret_cast< char *>(vert_normals), single_ind * 3 * sizeof(GLfloat));
104  // std::cout << "vert_normals pos: " << binary_file.tellg() << std::endl;
105  binary_file.read(reinterpret_cast< char *>(vert_uvs), single_ind * 3 * sizeof(GLfloat));
106  // std::cout << "vert_uvs pos: " << binary_file.tellg() << std::endl;
107  binary_file.read(reinterpret_cast< char *>(face_ind), nr_face_ind * sizeof(GLuint));
108  // std::cout << "face_ind pos: " << binary_file.tellg() << std::endl;
109  checkForErrors(binary_file);
110 
111  // close
112  binary_file.close();
113  checkForErrors(binary_file);
114  return 0;
115 }
116 
118 {
119  // extract directory name
120  int last_slash = this->file_path.find_last_of("/\\");
121  std::string dir_name = file_path.substr(0,last_slash);
122  std::string file_name_ext = file_path.substr(last_slash + 1);
123 
124  int last_dot = file_name_ext.find_last_of(".");
125  std::string file_name = file_name_ext.substr(0,last_dot);
126  std::string file_ext = file_name_ext.substr(last_dot + 1);
127 
128  std::string binary_file_name = dir_name + "/" + file_name + ".dat";
129  std::cout << "Writing to file : \"" << binary_file_name << "\" ..." << std::endl;
130 
131  // open stream
132  std::ofstream binary_file;
133  binary_file.open(binary_file_name, std::ofstream::binary | std::ofstream::out);
134  checkForErrors(binary_file);
135 
136  // write
137  binary_file.write(reinterpret_cast<const char *>(&single_ind), sizeof(unsigned int)); // vertex nr
138  binary_file.write(reinterpret_cast<const char *>(&nr_face_ind), sizeof(unsigned int)); // index nr
139  binary_file.write(reinterpret_cast<const char *>(vert_positions), single_ind * 3 * sizeof(GLfloat));
140  binary_file.write(reinterpret_cast<const char *>(vert_normals), single_ind * 3 * sizeof(GLfloat));
141  binary_file.write(reinterpret_cast<const char *>(vert_uvs), single_ind * 3 * sizeof(GLfloat));
142  binary_file.write(reinterpret_cast<const char *>(face_ind), nr_face_ind * sizeof(GLuint));
143  checkForErrors(binary_file);
144 
145  // close
146  binary_file.close();
147  checkForErrors(binary_file);
148  return 0;
149 }
150 
151 
152 void LoaderOBJ::checkForErrors(std::ios & _ofs)
153 {
154  if (_ofs.fail())
155  {
156  switch (errno)
157  {
158  case EACCES:
159  // this is set if the drive is not ready in DOS
160  std::cout << "Drive not ready or permission denied" << std::endl;
161  break;
162  case ENOENT:
163  std::cout << "Could not find this file" << std::endl;
164  break;
165  default:
166  perror("opening data file");
167  break;
168  }
169  system("PAUSE");
170  exit(-1);
171  }
172 }
173 
174 
176 // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //
177 // %% ------------------------------- OBJ PARSER --------------------------- %% //
178 // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //
180 
181 void LoaderOBJ::parseOBJ(std::FILE* _f)
182 {
183  // file->stream
184  std::ifstream file_stream(_f);
185  std::string file_line;
186  while (!file_stream.eof())
187  {
188  std::getline(file_stream, file_line);
189 
190  // --------------- EXTRACT VERTEX POSITIONS ----------------- //
191  if (!file_line.substr(0,2).compare("v "))
192  {
193  vertAttribContainer vert_pos = string2VertAttr(file_line);
194  for (unsigned int i = 0; i < vert_pos.size(); i++)
195  {
196  vert_positions_OBJ.push_back(vert_pos.at(i));
197  }
198  }
199  // ---------------- EXTRACT VERTEX NORMALS ------------------ //
200  else if (!file_line.substr(0,3).compare("vn "))
201  {
202  vertAttribContainer vert_norm = string2VertAttr(file_line);
203  for (unsigned int i = 0; i < vert_norm.size(); i++)
204  {
205  vert_normals_OBJ.push_back(vert_norm.at(i));
206  }
207  }
208  // ------------ EXTRACT TEXTURE COORDINATES ---------------- //
209  else if (!file_line.substr(0,3).compare("vt "))
210  {
211  vertAttribContainer text_coord = string2VertAttr(file_line);
212  for (unsigned int i = 0; i < text_coord.size(); i++)
213  {
214  // discard the third coord as it is a 2-dim value
215  if (i % 3 != 2)
216  {
217  vert_uvs_OBJ.push_back(text_coord.at(i));
218  }
219  }
220  }
221  // ----------------- EXTRACT FACE INDICES ------------------- //
222  else if (!file_line.substr(0,2).compare("f "))
223  {
224  indexContainer indices = string2Index(file_line);
225  for (unsigned int i = 0; i < indices.size(); i++)
226  {
227  face_ind_OBJ.push_back(indices.at(i));
228  }
229  }
230 
231  }
232  // std::cout << "Found " << vert_positions_OBJ.size() / 3 << " vert positions." << std::endl;
233  // std::cout << "Found " << vert_normals_OBJ.size() / 3 << " vert normals." << std::endl;
234  // std::cout << "Found " << vert_uvs_OBJ.size() / 2 << " texture coordinates." << std::endl;
235  // std::cout << "Found " << face_ind_OBJ.size() / 9 << " face indices." << std::endl;
236 
237  // ----------- CONVERT TO SINGLE INDEX GEOMETRY ------------- //
238  // i.e. a singe index per face vertex addresses all vertex attribute arrays
240 }
241 
243 {
244  // prepare a container for the floats
245  vertAttribContainer vert_pos;
246 
247  // open a string stream
248  std::stringstream line_ss(_line);
249 
250  // split the string along the SPACE delimiter
251  std::string str_item;
252  while(std::getline(line_ss,str_item,' '))
253  {
254  GLfloat num;
255  std::stringstream item_ss(str_item);
256  if (!(item_ss>>num).fail())
257  {
258  vert_pos.push_back(num);
259  }
260  }
261 
262  return vert_pos;
263 }
264 
266 
267  // prepare a container for the unsigned ints
269 
270  // open a string stream
271  std::stringstream line_ss(_line);
272 
273  // split the string along the SPACE delimiter to get to an index
274  // containg [position index]/[texture coordinate index]/[normal index] for one face vertex
275  // of a triangular face
276  std::string str_item;
277  while(std::getline(line_ss,str_item,' '))
278  {
279  std::stringstream item_ss(str_item);
280  std::string str_single_index;
281  while(std::getline(item_ss,str_single_index,'/'))
282  {
283  GLuint ind;
284  std::stringstream single_index_ss(str_single_index);
285  if (!(single_index_ss>>ind).fail())
286  {
287  face_ind.push_back(ind);
288  }
289  }
290  }
291 
292  return face_ind;
293 }
294 
295 bool LoaderOBJ::iCareEqual(const indexContainer & _ic1, const indexContainer & _ic2)
296 {
297  if (_ic1.size() < 2 || _ic2.size() < 2 || _ic1.size() != _ic2.size())
298  return false;
299 
300  bool equal = true;
301  // first 3 entries should be the old multi-index -> they should be compared
302  // excluding the last entry as it is the new unique single-index
303  for (unsigned int i = 0; i < _ic1.size()-1; i++)
304  {
305  equal = equal && (_ic1.at(i) == _ic2.at(i));
306  }
307  return equal;
308 }
309 
311 {
312 
313  // each face index in the OBJ file is a multi-index,
314  // consisting originally of 3 subindices [position index]/[texture coordinate index]/[normal index]
315  nr_face_ind = face_ind_OBJ.size() / 3;
316  face_ind = new GLuint[nr_face_ind];
317 
318  vert_positions = new GLfloat[nr_face_ind * 3];
319  vert_normals = new GLfloat[nr_face_ind * 3];
320  vert_uvs = new GLfloat[nr_face_ind * 2];
321 
322  // start a new indexing process
323  single_ind = 0;
324  for (unsigned int i = 0; i < nr_face_ind; i++)
325  {
326 
327  unsigned int ind_vertex_pos = face_ind_OBJ.at(i * 3);
328  unsigned int ind_texture_coord = face_ind_OBJ.at(i * 3 + 1);
329  unsigned int ind_vertex_normal = face_ind_OBJ.at(i * 3 + 2);
330  // std::cout << "at " << single_ind << ": " << ind_vertex_pos << "/" << ind_texture_coord << "/" << ind_vertex_normal << std::endl;
331 
332  // get next multi-index
333  indexContainer next_mult_index;
334  next_mult_index.push_back(ind_vertex_pos);
335  next_mult_index.push_back(ind_texture_coord);
336  next_mult_index.push_back(ind_vertex_normal);
337  next_mult_index.push_back(single_ind); // possibly new single index
338 
339  // if this multi-index was encountered already, copy its nr as a single index to the new face index array
340  // otherwise: add new entries in the new vertex attribute arrays
341  // add new single index in the new face index array
342  bool already_used = false;
343  for (unsigned int n = 0; n < new_mult_indices.size(); n++)
344  {
345  if (iCareEqual(new_mult_indices.at(n), next_mult_index))
346  {
347  already_used = true;
348  // copy the single index saved with the found multi-index
349  face_ind[i] = new_mult_indices.at(n).at(3);
350  break;
351  }
352  }
353  if (!already_used)
354  {
355  // record index combination for later usage (to avoid duplicates)
356  new_mult_indices.push_back(next_mult_index);
357  // add new single index to the new face index array
358  face_ind[i] = single_ind;
359 
360  // add new attribute records in the vertex attribute arrays
361  vert_positions[single_ind * 3] = vert_positions_OBJ.at((ind_vertex_pos - 1) * 3);
362  vert_positions[single_ind * 3 + 1] = vert_positions_OBJ.at((ind_vertex_pos - 1) * 3 + 1);
363  vert_positions[single_ind * 3 + 2] = vert_positions_OBJ.at((ind_vertex_pos - 1) * 3 + 2);
364 
365  vert_uvs[single_ind * 2] = vert_uvs_OBJ.at((ind_texture_coord - 1) * 2);
366  vert_uvs[single_ind * 2 + 1] = -vert_uvs_OBJ.at((ind_texture_coord - 1) * 2 + 1);
367 
368  vert_normals[single_ind * 3] = vert_normals_OBJ.at((ind_vertex_normal - 1) * 3);
369  vert_normals[single_ind * 3 + 1] = vert_normals_OBJ.at((ind_vertex_normal - 1) * 3 + 1);
370  vert_normals[single_ind * 3 + 2] = vert_normals_OBJ.at((ind_vertex_normal - 1) * 3 + 2);
371 
372  single_ind++;
373  }
374 
375  }
376 }