gKit2 light
mesh_data.cpp
Go to the documentation of this file.
1 
3 #include <cstdio>
4 #include <ctype.h>
5 #include <climits>
6 #include <cmath>
7 
8 #include <algorithm>
9 #include <map>
10 
11 #include "files.h"
12 #include "material_data.h"
13 #include "mesh_data.h"
14 
15 
16 MeshData read_mesh_data( const char *filename )
17 {
18  FILE *in= fopen(filename, "rt");
19  if(in == NULL)
20  {
21  printf("[error] loading mesh '%s'...\n", filename);
22  return MeshData();
23  }
24 
25  printf("loading mesh '%s'...\n", filename);
26 
27  MeshData data;
28  MaterialDataLib materials;
29  int default_material_id= -1;
30  int material_id= -1;
31 
32  std::vector<int> idp;
33  std::vector<int> idt;
34  std::vector<int> idn;
35 
36  char tmp[1024];
37  char line_buffer[1024];
38  bool error= true;
39  for(;;)
40  {
41  // charge une ligne du fichier
42  if(fgets(line_buffer, sizeof(line_buffer), in) == NULL)
43  {
44  error= false; // fin du fichier, pas d'erreur detectee
45  break;
46  }
47 
48  // force la fin de la ligne, au cas ou
49  line_buffer[sizeof(line_buffer) -1]= 0;
50 
51  // saute les espaces en debut de ligne
52  char *line= line_buffer;
53  while(*line && isspace(*line))
54  line++;
55 
56  if(line[0] == 'v')
57  {
58  float x, y, z;
59  if(line[1] == ' ') // position x y z
60  {
61  if(sscanf(line, "v %f %f %f", &x, &y, &z) != 3)
62  break;
63  data.positions.push_back( vec3(x, y, z) );
64  }
65  else if(line[1] == 'n') // normal x y z
66  {
67  if(sscanf(line, "vn %f %f %f", &x, &y, &z) != 3)
68  break;
69  data.normals.push_back( vec3(x, y, z) );
70  }
71  else if(line[1] == 't') // texcoord x y
72  {
73  if(sscanf(line, "vt %f %f", &x, &y) != 2)
74  break;
75  data.texcoords.push_back( vec2(x, y) );
76  }
77  }
78 
79  else if(line[0] == 'f') // triangle a b c, les sommets sont numerotes a partir de 1 ou de la fin du tableau (< 0)
80  {
81  idp.clear();
82  idt.clear();
83  idn.clear();
84 
85  int next;
86  for(line= line +1; ; line= line + next)
87  {
88  idp.push_back(0);
89  idt.push_back(0);
90  idn.push_back(0); // 0: invalid index
91 
92  next= 0;
93  if(sscanf(line, " %d/%d/%d %n", &idp.back(), &idt.back(), &idn.back(), &next) == 3)
94  continue;
95  else if(sscanf(line, " %d/%d %n", &idp.back(), &idt.back(), &next) == 2)
96  continue;
97  else if(sscanf(line, " %d//%d %n", &idp.back(), &idn.back(), &next) == 2)
98  continue;
99  else if(sscanf(line, " %d %n", &idp.back(), &next) == 1)
100  continue;
101  else if(next == 0) // fin de ligne
102  break;
103  }
104 
105  // force une matiere par defaut, si necessaire
106  if(material_id == -1)
107  {
108  if(default_material_id == -1)
109  {
110  // creer une matiere par defaut
111  default_material_id= data.materials.size();
112  data.materials.push_back( MaterialData() );
113  }
114 
115  material_id= default_material_id;
116  printf("usemtl default\n");
117  }
118 
119  // triangule la face, construit les triangles 0 1 2, 0 2 3, 0 3 4, etc
120  for(int v= 2; v +1 < (int) idp.size(); v++)
121  {
122  int idv[3]= { 0, v -1, v };
123  for(int i= 0; i < 3; i++)
124  {
125  int k= idv[i];
126  int p= (idp[k] < 0) ? (int) data.positions.size() + idp[k] : idp[k] -1;
127  int t= (idt[k] < 0) ? (int) data.texcoords.size() + idt[k] : idt[k] -1;
128  int n= (idn[k] < 0) ? (int) data.normals.size() + idn[k] : idn[k] -1;
129 
130  if(p < 0 || p >= (int) data.positions.size())
131  break; // error
132 
133  // conserve les indices du sommet
134  data.position_indices.push_back(p);
135  data.texcoord_indices.push_back(t);
136  data.normal_indices.push_back(n);
137  }
138 
139  // matiere du triangle...
140  data.material_indices.push_back(material_id);
141  }
142  }
143 
144  else if(line[0] == 'm')
145  {
146  if(sscanf(line, "mtllib %[^\r\n]", tmp) == 1)
147  {
148  materials= read_material_data( std::string(pathname(filename) + tmp).c_str() );
149  data.materials= materials.data;
150  }
151  }
152 
153  else if(line[0] == 'u')
154  {
155  if(sscanf(line, "usemtl %[^\r\n]", tmp) == 1)
156  {
157  material_id= -1;
158  for(unsigned int i= 0; i < (unsigned int) materials.names.size(); i++)
159  if(materials.names[i] == tmp)
160  material_id= i;
161 
162  if(material_id == -1)
163  {
164  // force une matiere par defaut, si necessaire
165  if(default_material_id == -1)
166  {
167  // creer une matiere par defaut
168  default_material_id= data.materials.size();
169  data.materials.push_back( MaterialData() );
170  }
171 
172  material_id= default_material_id;
173  }
174  }
175  }
176  }
177 
178  fclose(in);
179 
180  if(error)
181  printf("loading mesh '%s'...\n[error]\n%s\n\n", filename, line_buffer);
182 
183  printf(" %d positions, %d texcoords, %d normals, %d triangles\n",
184  (int) data.positions.size(), (int) data.texcoords.size(), (int) data.normals.size(), (int) data.material_indices.size());
185 
186  return data;
187 }
188 
189 
190 static
191 std::string normalize_path( std::string file )
192 {
193 #ifndef WIN32
194  std::replace(file.begin(), file.end(), '\\', '/'); // linux, macos : remplace les \ par /.
195 #else
196  std::replace(file.begin(), file.end(), '/', '\\'); // windows : remplace les / par \.
197 #endif
198  return file;
199 }
200 
201 
202 MaterialDataLib read_material_data( const char *filename )
203 {
204  MaterialDataLib materials;
205 
206  FILE *in= fopen(filename, "rt");
207  if(in == NULL)
208  {
209  printf("[error] loading materials '%s'...\n", filename);
210  return materials;
211  }
212 
213  printf("loading materials '%s'...\n", filename);
214 
215  MaterialData *material= NULL;
216  std::string path= pathname(filename);
217 
218  char tmp[1024];
219  char line_buffer[1024];
220  bool error= true;
221  for(;;)
222  {
223  // charge une ligne du fichier
224  if(fgets(line_buffer, sizeof(line_buffer), in) == NULL)
225  {
226  error= false; // fin du fichier, pas d'erreur detectee
227  break;
228  }
229 
230  // force la fin de la ligne, au cas ou
231  line_buffer[sizeof(line_buffer) -1]= 0;
232 
233  // saute les espaces en debut de ligne
234  char *line= line_buffer;
235  while(*line && isspace(*line))
236  line++;
237 
238  if(line[0] == 'n')
239  {
240  if(sscanf(line, "newmtl %[^\r\n]", tmp) == 1)
241  {
242  materials.names.push_back( tmp );
243  materials.data.push_back( MaterialData() );
244  material= &materials.data.back();
245  }
246  }
247 
248  if(material == NULL)
249  continue;
250 
251  if(line[0] == 'K')
252  {
253  float r, g, b;
254  if(sscanf(line, "Kd %f %f %f", &r, &g, &b) == 3)
255  material->diffuse= Color(r, g, b);
256  else if(sscanf(line, "Ks %f %f %f", &r, &g, &b) == 3)
257  material->specular= Color(r, g, b);
258  else if(sscanf(line, "Ke %f %f %f", &r, &g, &b) == 3)
259  material->emission= Color(r, g, b);
260  }
261 
262  else if(line[0] == 'N')
263  {
264  float n;
265  if(sscanf(line, "Ns %f", &n) == 1) // Ns, puissance / concentration du reflet, modele blinn phong
266  material->ns= n;
267  }
268 
269  else if(line[0] == 'm')
270  {
271  if(sscanf(line, "map_Kd %[^\r\n]", tmp) == 1)
272  material->diffuse_filename= normalize_path(path + tmp);
273 
274  if(sscanf(line, "map_Ks %[^\r\n]", tmp) == 1)
275  material->ns_filename= normalize_path(path + tmp);
276 
277  //~ if(sscanf(line, "map_bump %[^\r\n]", tmp) == 1)
278  //~ material->normal_filename= tmp;
279  }
280  }
281 
282  fclose(in);
283  if(error)
284  printf("[error] parsing line :\n%s\n", line_buffer);
285 
286  return materials;
287 }
288 
289 
290 void bounds( const MeshData& data, Point& pmin, Point& pmax )
291 {
292  if(data.positions.size() < 1)
293  return;
294 
295  pmin= Point(data.positions[0]);
296  pmax= pmin;
297 
298  for(int i= 1; i < (int) data.positions.size(); i++)
299  {
300  vec3 p= data.positions[i];
301  pmin= Point( std::min(pmin.x, p.x), std::min(pmin.y, p.y), std::min(pmin.z, p.z) );
302  pmax= Point( std::max(pmax.x, p.x), std::max(pmax.y, p.y), std::max(pmax.z, p.z) );
303  }
304 }
305 
306 
307 void normals( MeshData& data )
308 {
309 printf("[mesh] building normals...\n");
310 
311  // une normale par position
312  std::vector<Vector> normals(data.positions.size(), Vector());
313  for(int i= 0; i + 2 < (int) data.position_indices.size(); i+= 3)
314  {
315  // positions des sommets du triangle
316  Point a= Point(data.positions[data.position_indices[i]]);
317  Point b= Point(data.positions[data.position_indices[i +1]]);
318  Point c= Point(data.positions[data.position_indices[i +2]]);
319 
320  // normale geometrique
321  Vector n= normalize(cross(normalize(b - a), normalize(c - a)));
322  float anglea= std::acos(dot(normalize(b - a), normalize(c - a)));
323  //~ float anglea= std::atan2(length(cross(normalize(b - a), normalize(c - a))), dot(normalize(b - a), normalize(c - a)));
324  //~ if(anglea < 0) anglea+= float(2*M_PI);
325  float angleb= std::acos(dot(normalize(c - b), normalize(a - b)));
326  //~ float angleb= std::atan2(length(cross(normalize(c - b), normalize(a - b))), dot(normalize(c - b), normalize(a - b)));
327  //~ if(angleb < 0) angleb+= float(2*M_PI);
328  float anglec= std::acos(dot(normalize(a - c), normalize(b - c)));
329  //~ float anglec= std::atan2(length(cross(normalize(a - c), normalize(b - c))), dot(normalize(a - c), normalize(b - c)));
330  //~ if(anglec < 0) anglec+= float(2*M_PI);
331 
332  // somme la normale sur les sommets du triangle
333  normals[data.position_indices[i]]= normals[data.position_indices[i]] + n * anglea;
334  normals[data.position_indices[i +1]]= normals[data.position_indices[i +1]] + n * angleb;
335  normals[data.position_indices[i +2]]= normals[data.position_indices[i +2]] + n * anglec;
336  }
337 
338  // copie
339  data.normals.clear();
340  data.normals.reserve(normals.size());
341  for(int i= 0; i < (int) normals.size(); i++)
342  data.normals.push_back( vec3(normalize(normals[i])) );
343 
344  // re-indexe les sommets
345  for(int i= 0; i < (int) data.normal_indices.size(); i++)
346  data.normal_indices[i]= data.position_indices[i];
347 }
348 
349 
351 {
352  MeshData mesh;
353  mesh.materials= data.materials;
354  mesh.material_indices= data.material_indices;
355 
356  mesh.positions.reserve(data.positions.size());
357  mesh.texcoords.reserve(data.texcoords.size());
358  mesh.normals.reserve(data.normals.size());
359 
360  for(int i= 0; i < (int) data.position_indices.size(); i++)
361  {
362  mesh.positions.push_back( data.positions[data.position_indices[i]] );
363  if(data.texcoord_indices[i] >= 0)
364  mesh.texcoords.push_back( data.texcoords[data.texcoord_indices[i]] );
365  if(data.normal_indices[i] >= 0)
366  mesh.normals.push_back( data.normals[data.normal_indices[i]] );
367  }
368 
369  data.position_indices.clear();
370  data.texcoord_indices.clear();
371  data.normal_indices.clear();
372 
373  return mesh;
374 }
375 
const std::vector< unsigned int > & material_indices() const
renvoie les indices des matieres des triangles.
Definition: mesh.cpp:287
const Materials & materials() const
renvoie la description des matieres.
Definition: mesh.cpp:267
void printf(Text &text, const int px, const int py, const char *format,...)
affiche un texte a la position x, y. meme utilisation que printf().
Definition: text.cpp:140
Point max(const Point &a, const Point &b)
renvoie la plus grande composante de chaque point. x, y, z= max(a.x, b.x), max(a.y,...
Definition: vec.cpp:35
Point min(const Point &a, const Point &b)
renvoie la plus petite composante de chaque point. x, y, z= min(a.x, b.x), min(a.y,...
Definition: vec.cpp:30
float dot(const Vector &u, const Vector &v)
renvoie le produit scalaire de 2 vecteurs.
Definition: vec.cpp:137
Vector normalize(const Vector &v)
renvoie un vecteur unitaire / longueur == 1.
Definition: vec.cpp:123
Vector cross(const Vector &u, const Vector &v)
renvoie le produit vectoriel de 2 vecteurs.
Definition: vec.cpp:129
charge les textures utiilisees par un ensemble de matieres.
MaterialDataLib read_material_data(const char *filename)
charge un ensemble de matieres texturees.
Definition: mesh_data.cpp:202
void normals(MeshData &data)
(re-) calcule les normales des sommets. utiliser avant les reindexations, cf indices() et vertices().
Definition: mesh_data.cpp:307
MeshData read_mesh_data(const char *filename)
charge un fichier wavefront .obj et renvoie les donnees.
Definition: mesh_data.cpp:16
MeshData vertices(MeshData &data)
construit les sommets. prepare l'affichage openGL, avec glDrawArrays().
Definition: mesh_data.cpp:350
void bounds(const MeshData &data, Point &pmin, Point &pmax)
renvoie l'englobant.
Definition: mesh_data.cpp:290
representation des donnees d'un fichier wavefront .obj
std::string pathname(const std::string &filename)
Definition: files.cpp:68
ensemble de matieres texturees.
Definition: mesh_data.h:40
representation d'une couleur (rgba) transparente ou opaque.
Definition: color.h:14
representation d'une matiere texturee.
Definition: mesh_data.h:16
Color specular
couleur du reflet
Definition: mesh_data.h:23
Color emission
pour une source de lumiere
Definition: mesh_data.h:25
std::string diffuse_filename
nom de la texture diffuse
Definition: mesh_data.h:20
float ns
exposant pour les reflets blinn-phong
Definition: mesh_data.h:26
Color diffuse
couleur diffuse
Definition: mesh_data.h:17
std::string ns_filename
nom de la texture exposant
Definition: mesh_data.h:28
representation d'un point 3d.
Definition: vec.h:21
representation d'un vecteur 3d.
Definition: vec.h:59
vecteur generique, utilitaire.
Definition: vec.h:131
vecteur generique, utilitaire.
Definition: vec.h:146