gKit2 light
gltf.cpp
1 
2 #include <cstdio>
3 #include <cstring>
4 #include <cfloat>
5 
6 #include "files.h"
7 #include "texture.h"
8 #include "image_io.h"
9 
10 #include "cgltf.h"
11 #include "gltf.h"
12 
13 #include "SDL2/SDL_rwops.h"
14 #include "SDL2/SDL_surface.h"
15 #include "SDL2/SDL_image.h"
16 
17 
18 
19 Mesh read_gltf_mesh( const char *filename )
20 {
21  printf("loading glTF mesh '%s'...\n", filename);
22 
23  cgltf_options options= { };
24  cgltf_data *data= nullptr;
25  cgltf_result code= cgltf_parse_file(&options, filename, &data);
26  if(code != cgltf_result_success)
27  {
28  printf("[error] loading glTF mesh '%s'...\n", filename);
29  return Mesh::error();
30  }
31 
32  if(cgltf_validate(data) != cgltf_result_success)
33  {
34  printf("[error] invalid glTF mesh '%s'...\n", filename);
35  return Mesh::error();
36  }
37 
38  code= cgltf_load_buffers(&options, data, filename);
39  if(code != cgltf_result_success)
40  {
41  printf("[error] loading glTF buffers...\n");
42  return Mesh::error();
43  }
44 
45  //
46  std::vector<unsigned> indices;
47  std::vector<int> material_indices;
48  std::vector<vec3> positions;
49  std::vector<vec2> texcoords;
50  std::vector<vec3> normals;
51  std::vector<vec4> colors;
52 
53  Materials materials;
54 
55  // textures
56  for(unsigned i= 0; i < data->images_count; i++)
57  {
58  if(data->images[i].uri)
59  {
60  printf("texture '%s'...\n", data->images[i].uri);
61  materials.insert_texture(data->images[i].uri);
62  }
63  else if(data->images[i].buffer_view)
64  {
65  //~ cgltf_buffer_view *view= data->images[i].buffer_view;
66  //~ assert(view->buffer->data);
67  //~ printf(" [%u] %s offset %lu size %lu, type '%s'\n", i, data->images[i].name, view->offset, view->size, data->images[i].mime_type);
68 
69  //~ SDL_RWops *read= SDL_RWFromConstMem((uint8_t *) view->buffer->data + view->offset, view->size);
70  //~ assert(read);
71 
72  // extraire la texture du glb...
73 
74  cgltf_buffer_view *view= data->images[i].buffer_view;
75  //~ printf("buffer %d/%d, offset %lu size %lu\n", int(std::distance(data->buffers, view->buffer)), int(data->buffers_count), view->offset, view->size);
76  //~ printf(" type '%s'\n", data->images[i].mime_type);
77 
78  if(!view->buffer->uri)
79  {
80  char tmp[1024];
81  if(data->images[i].name && data->images[i].name[0])
82  sprintf(tmp, "%s%s", pathname(filename).c_str(), data->images[i].name);
83  else
84  sprintf(tmp, "%stexture%d.png", pathname(filename).c_str(), i);
85 
86  printf("packed texture '%s'...\n", tmp);
87  materials.insert_texture(tmp);
88 
89  #if 0
90  if(strcmp(data->images[i].mime_type, "image/png") == 0)
91  strcat(tmp, ".png");
92  else if(strcmp(data->images[i].mime_type, "image/jpg") == 0)
93  strcat(tmp, ".jpg");
94  else
95  strcat(tmp, ".raw"); // ??
96 
97  printf("unpacking glb texture '%s' to '%s'...\n", data->images[i].name, tmp);
98 
99  FILE *out= fopen(tmp, "wb");
100  if(out)
101  {
102  if(fwrite((char *) view->buffer->data + view->offset, view->size, 1, out) != 1)
103  printf("[error] unpacking glb texture '%s' to '%s'...\n", data->images[i].name, tmp);
104 
105  fclose(out);
106  }
107 
108  // ultra moche, utiliser les RWops de sdl... pour lire l'image directement en memoire...
109  // retourner l'image, origine en bas a gauche pour opengl
110  ImageData image= read_image_data(tmp);
111  image= flipY(image);
112 
113  if(char *ext= strrchr(tmp, '.'))
114  strcpy(ext, "_flip.png");
115 
116  printf("writing flipped texture '%s'...\n", tmp);
117  write_image_data(image, tmp);
118 
119  materials.insert_texture(tmp);
120  assert(data->images[i].uri == nullptr);
121  data->images[i].uri= strdup(tmp); // nomme la texture / cf analyse des matieres
122  #endif
123  }
124  }
125  }
126 
127  // materials
128  for(unsigned i= 0; i < data->materials_count; i++)
129  {
130  cgltf_material *material= &data->materials[i];
131  //~ printf("materials[%u]: '%s'\n", i, material->name);
132 
133  Material m(Color(0.8));
134  if(material->has_pbr_metallic_roughness)
135  {
136  cgltf_pbr_metallic_roughness *pbr= &material->pbr_metallic_roughness;
137  //~ printf(" pbr metallic roughness\n");
138  //~ printf(" base color %f %f %f %f\n", pbr->base_color_factor[0], pbr->base_color_factor[1], pbr->base_color_factor[2], pbr->base_color_factor[3]);
139  //~ printf(" texture %d\n", pbr->base_color_texture.texture ? int(std::distance(data->images, pbr->base_color_texture.texture->image)) : -1);
140  //~ printf(" metallic %f, roughness %f\n", pbr->metallic_factor, pbr->roughness_factor);
141  //~ printf(" texture %d\n", pbr->metallic_roughness_texture.texture ? int(std::distance(data->images, pbr->metallic_roughness_texture.texture->image)) : -1);
142 
143  Color color= Color(pbr->base_color_factor[0], pbr->base_color_factor[1], pbr->base_color_factor[2], pbr->base_color_factor[3]);
144  float metallic= pbr->metallic_factor;
145  float roughness= pbr->roughness_factor;
146 
147  // conversion metallic-roughness vers diffuse-specular + Blinn-Phong
148  // metaux { diffuse= black, specular= color }
149  // non - metaux { diffuse= color, specular= 0.04 }
150  m.diffuse= color * (1 - metallic) + metallic * Black();
151  m.specular= Color(0.04) * (1 - metallic) + color * metallic;
152 
153  // conversion roughness vers exposant Blinn-Phong
154  m.ns= 2 / (roughness * roughness * roughness * roughness) - 2;
155  if(m.ns < float(1.1))
156  m= Material(m.diffuse);
157  // les valeurs sont habituellement dans les textures metallic_roughness... utiliser une matiere diffuse + texture...
158 
159  //~ printf(" | diffuse %f %f %f, specular %f %f %f, ns %f\n",
160  //~ m.diffuse.r, m.diffuse.g, m.diffuse.b,
161  //~ m.specular.r, m.specular.g, m.specular.b,
162  //~ m.ns);
163 
164  if(pbr->base_color_texture.texture && pbr->base_color_texture.texture->image)
165  m.diffuse_texture= int(std::distance(data->images, pbr->base_color_texture.texture->image));
166  }
167 
168  if(!material->name)
169  {
170  char tmp[1024];
171  sprintf(tmp, "material%d", i);
172  material->name= strdup(tmp);
173  }
174 
175  materials.insert(m, material->name);
176  }
177 
178  bool mesh_has_texcoords= false;
179  bool mesh_has_normals= false;
180  bool mesh_has_colors= false;
181 
182  std::vector<float> buffer;
183  // parcourir les noeuds de la scene et transformer les meshs associes aux noeuds...
184  for(unsigned node_id= 0; node_id < data->nodes_count; node_id++)
185  {
186  cgltf_node *node= &data->nodes[node_id];
187  if(node->mesh== nullptr)
188  // pas de mesh associe
189  continue;
190 
191  // transformation vers la scene
192  float model_matrix[16];
193  cgltf_node_transform_world(node, model_matrix); // transformation globale
194 
195  Transform model;
196  model.column_major(model_matrix); // gltf organise les 16 floats par colonne...
197  Transform normal= model.normal(); // transformation pour les normales
198  //~ Transform normal= model; // transformation pour les normales
199 
200  cgltf_mesh *mesh= node->mesh;
201  // parcourir les groupes de triangles du mesh...
202  for(unsigned primitive_id= 0; primitive_id < mesh->primitives_count; primitive_id++)
203  {
204  cgltf_primitive *primitives= &mesh->primitives[primitive_id];
205  assert(primitives->type == cgltf_primitive_type_triangles);
206 
207  bool primitive_has_texcoords= false;
208  bool primitive_has_normals= false;
209  bool primitive_has_colors= false;
210  unsigned offset= positions.size();
211 
212  // matiere associee au groupe de triangles
213  int material_id= -1;
214  if(primitives->material)
215  {
216  assert(material_id < materials.count());
217  assert(materials.find(primitives->material->name) != -1);
218  material_id= materials.find(primitives->material->name);
219  }
220 
221  // indices
222  if(primitives->indices)
223  {
224  for(unsigned i= 0; i < primitives->indices->count; i++)
225  indices.push_back(offset + cgltf_accessor_read_index(primitives->indices, i));
226  assert(indices.size() % 3 == 0);
227 
228  // un indice de matiere par triplet d'indices / par triangle
229  for(unsigned i= 0; i+2 < primitives->indices->count; i+= 3)
230  material_indices.push_back(material_id);
231  assert(indices.size() / 3 == material_indices.size());
232  }
233 
234  // attributs
235  for(unsigned attribute_id= 0; attribute_id < primitives->attributes_count; attribute_id++)
236  {
237  cgltf_attribute *attribute= &primitives->attributes[attribute_id];
238 
239  if(attribute->type == cgltf_attribute_type_position)
240  {
241  assert(attribute->data->type == cgltf_type_vec3);
242 
243  buffer.resize(cgltf_accessor_unpack_floats(attribute->data, nullptr, 0));
244  cgltf_accessor_unpack_floats(attribute->data, buffer.data(), buffer.size());
245 
246  // transforme les positions des sommets
247  for(unsigned i= 0; i+2 < buffer.size(); i+= 3)
248  positions.push_back( model(Point(buffer[i], buffer[i+1], buffer[i+2])) );
249  }
250 
251  if(attribute->type == cgltf_attribute_type_normal)
252  {
253  assert(attribute->data->type == cgltf_type_vec3);
254 
255  primitive_has_normals= true;
256  if(mesh_has_normals == false)
257  {
258  mesh_has_normals= true;
259  // insere une normale par defaut pour tous les sommets precedents...
260  for(unsigned i= 0; i < offset; i++)
261  normals.push_back( vec3() );
262  }
263 
264  buffer.resize(cgltf_accessor_unpack_floats(attribute->data, nullptr, 0));
265  cgltf_accessor_unpack_floats(attribute->data, buffer.data(), buffer.size());
266 
267  // transforme les normales des sommets
268  for(unsigned i= 0; i+2 < buffer.size(); i+= 3)
269  normals.push_back( normal(Vector(buffer[i], buffer[i+1], buffer[i+2])) );
270  //~ assert(normals.size() == positions.size());
271  }
272 
273  if(attribute->type == cgltf_attribute_type_texcoord)
274  {
275  assert(attribute->data->type == cgltf_type_vec2);
276 
277  primitive_has_texcoords= true;
278  if(mesh_has_texcoords == false)
279  {
280  mesh_has_texcoords= true;
281  // insere des texcoords par defaut pour tous les sommets precedents
282  for(unsigned i= 0; i < offset; i++)
283  texcoords.push_back( vec2() );
284  }
285 
286  buffer.resize(cgltf_accessor_unpack_floats(attribute->data, nullptr, 0));
287  cgltf_accessor_unpack_floats(attribute->data, buffer.data(), buffer.size());
288 
289  for(unsigned i= 0; i+1 < buffer.size(); i+= 2)
290  texcoords.push_back( vec2(buffer[i], buffer[i+1]) );
291  //~ assert(texcoords.size() == positions.size());
292  }
293 
294  if(attribute->type == cgltf_attribute_type_color)
295  {
296  assert(attribute->data->type == cgltf_type_vec4);
297 
298  primitive_has_colors= true;
299  if(mesh_has_colors == false)
300  {
301  mesh_has_colors= true;
302  // insere une couleur par defaut pour tous les sommtes precedents
303  for(unsigned i= 0; i < offset; i++)
304  colors.push_back( vec4(1, 1, 1, 1) );
305  }
306 
307  buffer.resize(cgltf_accessor_unpack_floats(attribute->data, nullptr, 0));
308  cgltf_accessor_unpack_floats(attribute->data, buffer.data(), buffer.size());
309  for(unsigned i= 0; i+3 < buffer.size(); i+= 4)
310  colors.push_back( vec4(buffer[i], buffer[i+1], buffer[i+2], buffer[i+3]) );
311  assert(colors.size() == positions.size());
312  }
313  }
314 
315  // complete la description des attributs par defaut...
316  if(mesh_has_texcoords && primitive_has_texcoords == false)
317  for(unsigned i= offset; i < positions.size(); i++)
318  texcoords.push_back( vec2() );
319 
320  if(mesh_has_normals && primitive_has_normals == false)
321  for(unsigned i= offset; i < positions.size(); i++)
322  normals.push_back( vec3() );
323 
324  if(mesh_has_colors && primitive_has_colors == false)
325  for(unsigned i= offset; i < positions.size(); i++)
326  colors.push_back( vec4(1, 1, 1, 1) );
327  }
328  }
329 
330  cgltf_free(data);
331 
332  // reconstruit le mesh...
333  Mesh mesh(GL_TRIANGLES);
334 
335  // 1. les sommets et les attributs, si necessaire...
336  bool has_texcoords= (texcoords.size() == positions.size());
337  bool has_normals= (normals.size() == positions.size());
338  bool has_colors= (colors.size() == positions.size());
339 
340  printf("gltf %d positions, %d texcoords, %d normals\n", int(positions.size()), int(texcoords.size()), int(normals.size()));
341 
342  for(unsigned i= 0; i < positions.size(); i++)
343  {
344  if(has_texcoords) mesh.texcoord(texcoords[i]);
345  if(has_normals) mesh.normal(normals[i]);
346  if(has_colors) mesh.color(colors[i]);
347 
348  mesh.vertex(positions[i]);
349  }
350 
351  // 2. les triangles et leurs matieres, si necessaire...
352  mesh.materials(materials);
353  bool has_materials= (materials.count() > 0) && (indices.size() / 3 == material_indices.size());
354  for(unsigned i= 0; i+2 < indices.size(); i+= 3)
355  {
356  if(has_materials) mesh.material(material_indices[i / 3]);
357 
358  mesh.triangle(indices[i], indices[i+1], indices[i+2]);
359  }
360  // \bug si les triangles ne sont pas indexes, pas de matieres dans le mesh...
361 
362  return mesh;
363 }
364 
365 
366 
367 static
368 std::vector<GLTFCamera> read_cameras( cgltf_data *data )
369 {
370  std::vector<GLTFCamera> cameras;
371  for(unsigned i= 0; i < data->nodes_count; i++)
372  {
373  cgltf_node *node= &data->nodes[i];
374  if(node->camera == nullptr)
375  continue;
376 
377  cgltf_camera_perspective *perspective= &node->camera->data.perspective;
378 
379  //~ if(perspective->has_aspect_ratio)
380  //~ printf(" aspect ratio %f\n", perspective->aspect_ratio);
381  //~ printf(" yfov %f\n", perspective->yfov);
382  //~ printf(" znear %f", perspective->znear);
383  //~ if(perspective->has_zfar)
384  //~ printf(", zfar %f", perspective->zfar);
385  //~ printf("\n");
386 
387  Transform projection= Perspective(degrees(perspective->yfov), perspective->aspect_ratio, perspective->znear, perspective->zfar);
388 
389  float model_matrix[16];
390  cgltf_node_transform_world(node, model_matrix); // transformation globale
391 
392  Transform model;
393  model.column_major(model_matrix); // gltf organise les 16 floats par colonne...
394  Transform view= Inverse(model); // view= inverse(model)
395 
396  cameras.push_back( { degrees(perspective->yfov), perspective->aspect_ratio, perspective->znear, perspective->zfar, view, projection } );
397  }
398 
399  return cameras;
400 }
401 
402 std::vector<GLTFCamera> read_gltf_cameras( const char *filename )
403 {
404  printf("loading glTF camera '%s'...\n", filename);
405 
406  cgltf_options options= { };
407  cgltf_data *data= nullptr;
408  cgltf_result code= cgltf_parse_file(&options, filename, &data);
409  if(code != cgltf_result_success)
410  {
411  printf("[error] loading glTF mesh '%s'...\n", filename);
412  return {};
413  }
414 
415  if(cgltf_validate(data) != cgltf_result_success)
416  {
417  printf("[error] invalid glTF mesh '%s'...\n", filename);
418  return {};
419  }
420 
421  if(data->cameras_count == 0)
422  {
423  printf("[warning] no camera...\n");
424  return {};
425  }
426 
427  std::vector<GLTFCamera> cameras= read_cameras(data);
428  cgltf_free(data);
429  return cameras;
430 }
431 
432 
433 static
434 std::vector<GLTFLight> read_lights( cgltf_data *data )
435 {
436  std::vector<GLTFLight> lights;
437  // retrouve les transformations associees aux sources
438  for(unsigned i= 0; i < data->nodes_count; i++)
439  {
440  cgltf_node *node= &data->nodes[i];
441  if(node->light == nullptr)
442  continue;
443 
444  //~ int light_id= int(std::distance(data->lights, node->light));
445  //~ printf("light[%u] attached to node[%u]...\n", light_id, i);
446 
447  // position de la source
448  float model_matrix[16];
449  cgltf_node_transform_world(node, model_matrix); // transformation globale
450 
451  Transform model;
452  model.column_major(model_matrix); // gltf organise les 16 floats par colonne...
453  Point position= model(Origin());
454 
455  // proprietes de la source
456  cgltf_light *light= node->light;
457  //~ printf(" position %f %f %f\n", position.x, position.y, position.z);
458  //~ printf(" emission %f %f %f\n", light->color[0], light->color[1], light->color[2]);
459  //~ printf(" intensity %f\n", light->intensity);
460 
461  lights.push_back( { position, Color(light->color[0], light->color[1], light->color[2]), light->intensity } );
462  }
463 
464  return lights;
465 }
466 
467 std::vector<GLTFLight> read_gltf_lights( const char *filename )
468 {
469  printf("loading glTF lights '%s'...\n", filename);
470 
471  cgltf_options options= { };
472  cgltf_data *data= nullptr;
473  cgltf_result code= cgltf_parse_file(&options, filename, &data);
474  if(code != cgltf_result_success)
475  {
476  printf("[error] loading glTF mesh '%s'...\n", filename);
477  return {};
478  }
479 
480  if(cgltf_validate(data) != cgltf_result_success)
481  {
482  printf("[error] invalid glTF mesh '%s'...\n", filename);
483  return {};
484  }
485 
486  if(data->lights_count == 0)
487  {
488  printf("[warning] no lights...\n");
489  return {};
490  }
491 
492  std::vector<GLTFLight> lights= read_lights(data);
493  cgltf_free(data);
494  return lights;
495 }
496 
497 
498 static
499 std::vector<GLTFMaterial> read_materials( cgltf_data *data )
500 {
501  std::vector<GLTFMaterial> materials;
502  for(unsigned i= 0; i < data->materials_count; i++)
503  {
504  cgltf_material *material= &data->materials[i];
505  //~ printf("materials[%u] '%s'\n", i, material->name);
506 
507  GLTFMaterial m= { };
508  m.color= Color(0.8, 0.8, 0.8, 1);
509  m.metallic= 0;
510  m.roughness= 1;
511  m.transmission= 0;
512  m.ior= 0;
513  m.specular= 0;
514  m.specular_color= Black();
515  m.thickness= 0;
516  m.attenuation_distance= 0;
517  m.attenuation_color= Black();
518  m.color_texture= -1;
520  m.occlusion_texture= -1;
521  m.normal_texture= -1;
522  m.emission_texture= -1;
523  m.transmission_texture= -1;
524  m.specular_texture= -1;
526  m.thickness_texture= -1;
527 
528  if(material->has_pbr_metallic_roughness)
529  {
530  cgltf_pbr_metallic_roughness *pbr= &material->pbr_metallic_roughness;
531 
532  m.color= Color(pbr->base_color_factor[0], pbr->base_color_factor[1], pbr->base_color_factor[2], pbr->base_color_factor[3]);
533  if(pbr->base_color_texture.texture && pbr->base_color_texture.texture->image)
534  m.color_texture= int(std::distance(data->images, pbr->base_color_texture.texture->image));
535 
536  m.metallic= pbr->metallic_factor;
537  m.roughness= pbr->roughness_factor;
538  if(pbr->metallic_roughness_texture.texture && pbr->metallic_roughness_texture.texture->image)
539  m.metallic_roughness_texture= int(std::distance(data->images, pbr->metallic_roughness_texture.texture->image));
540 
541  printf(" pbr metallic roughness\n");
542  printf(" base color %f %f %f, texture %d\n", m.color.r, m.color.g, m.color.b, m.color_texture);
543  printf(" metallic %f, roughness %f, texture %d\n", m.metallic, m.roughness, m.metallic_roughness_texture);
544  }
545  if(material->has_clearcoat)
546  printf(" clearcoat\n");
547  //~ if(material->has_sheen)
548  //~ printf(" sheen\n");
549 
550  if(material->normal_texture.texture && material->normal_texture.texture->image)
551  {
552  //~ printf(" normal texture %d\n", int(std::distance(data->images, material->normal_texture.texture->image)));
553  m.normal_texture= int(std::distance(data->images, material->normal_texture.texture->image));
554  }
555 
556  //~ printf(" emissive color %f %f %f\n", material->emissive_factor[0], material->emissive_factor[1], material->emissive_factor[2]);
557  m.emission= Color(material->emissive_factor[0], material->emissive_factor[1], material->emissive_factor[2]);
558  if(material->emissive_texture.texture && material->emissive_texture.texture->image)
559  {
560  //~ printf(" texture %d\n", int(std::distance(data->images, material->emissive_texture.texture->image)));
561  m.emission_texture= int(std::distance(data->images, material->emissive_texture.texture->image));
562  }
563 
564 
565  if(material->has_ior)
566  {
567  m.ior= material->ior.ior;
568  if(m.ior == float(1.5))
569  m.ior= 0; // valeur par defaut
570 
571  if(m.ior)
572  printf(" ior %f\n", m.ior);
573  }
574 
575  if(material->has_specular)
576  {
577  m.specular= material->specular.specular_factor;
578  if(material->specular.specular_texture.texture && material->specular.specular_texture.texture->image)
579  m.specular_texture= std::distance(data->images, material->specular.specular_texture.texture->image);
580 
581  m.specular_color= Color(material->specular.specular_color_factor[0], material->specular.specular_color_factor[1], material->specular.specular_color_factor[2]);
582  if(material->specular.specular_color_texture.texture && material->specular.specular_color_texture.texture->image)
583  m.specular_color_texture= std::distance(data->images, material->specular.specular_color_texture.texture->image);
584 
586  {
587  // parametres incoherents... valeur par defaut / desactive ce comportement
588  m.specular= 0;
589  m.specular_color= Black();
590  }
591 
592  if(m.specular)
593  printf(" specular %f color %f %f %f, texture %d\n", m.specular, m.specular_color.r, m.specular_color.g, m.specular_color.b, m.specular_texture);
594  }
595 
596  if(material->has_transmission)
597  {
598  m.transmission= material->transmission.transmission_factor;
599  if(material->transmission.transmission_texture.texture && material->transmission.transmission_texture.texture->image)
600  m.transmission_texture= std::distance(data->images, material->transmission.transmission_texture.texture->image);
601 
602  if(m.transmission)
603  printf(" transmission %f, texture %d\n", m.transmission, m.transmission_texture);
604  }
605 
606  if(material->has_volume)
607  {
608  m.thickness= material->volume.thickness_factor;
609  if(material->volume.thickness_texture.texture && material->volume.thickness_texture.texture->image)
610  m.thickness_texture= std::distance(data->images, material->volume.thickness_texture.texture->image);
611 
612  m.attenuation_distance= material->volume.attenuation_distance;
613  m.attenuation_color= Color(material->volume.attenuation_color[0], material->volume.attenuation_color[1], material->volume.attenuation_color[2]);
614  printf(" volume thickness %f, texture %d\n",m.thickness, m.thickness_texture);
615  printf(" volume attenation distance %f, color %f %f %f\n", m.attenuation_distance, m.attenuation_color.r, m.attenuation_color.g, m.attenuation_color.b);
616  }
617 
618  materials.push_back(m);
619  }
620 
621  return materials;
622 }
623 
624 std::vector<GLTFMaterial> read_gltf_materials( const char *filename )
625 {
626  printf("loading glTF materials '%s'...\n", filename);
627 
628  cgltf_options options= { };
629  cgltf_data *data= nullptr;
630  cgltf_result code= cgltf_parse_file(&options, filename, &data);
631  if(code != cgltf_result_success)
632  {
633  printf("[error] loading glTF mesh '%s'...\n", filename);
634  return {};
635  }
636 
637  if(cgltf_validate(data) != cgltf_result_success)
638  {
639  printf("[error] invalid glTF mesh '%s'...\n", filename);
640  return {};
641  }
642 
643  if(data->materials_count ==0)
644  {
645  printf("[warning] no materials...\n");
646  return {};
647  }
648 
649  std::vector<GLTFMaterial> materials= read_materials(data);
650  cgltf_free(data);
651  return materials;
652 }
653 
654 
655 std::vector<ImageData> read_gltf_images( const char *filename )
656 {
657  printf("loading glTF images '%s'...\n", filename);
658 
659  cgltf_options options= { };
660  cgltf_data *data= nullptr;
661  cgltf_result code= cgltf_parse_file(&options, filename, &data);
662  if(code != cgltf_result_success)
663  {
664  printf("[error] loading glTF mesh '%s'...\n", filename);
665  return {};
666  }
667 
668  if(cgltf_validate(data) != cgltf_result_success)
669  {
670  printf("[error] invalid glTF mesh '%s'...\n", filename);
671  return {};
672  }
673 
674  if(data->images_count == 0)
675  {
676  printf("[warning] no images...\n");
677  return {};
678  }
679 
680  // detecte s'il faut charger aussi les buffers...
681  for(unsigned i= 0; i < data->images_count; i++)
682  if(!data->images[i].uri)
683  {
684  code= cgltf_load_buffers(&options, data, filename);
685  if(code != cgltf_result_success)
686  {
687  printf("[error] loading glTF internal images...\n");
688  cgltf_free(data);
689  return {};
690  }
691 
692  break;
693  }
694 
695 
696  std::vector<ImageData> images(data->images_count);
697 
698 #pragma omp parallel for schedule(dynamic, 1)
699  for(unsigned i= 0; i < data->images_count; i++)
700  {
701  if(data->images[i].uri)
702  {
703  //~ printf(" [%u] %s\n", i, data->images[i].uri);
704  std::string image_filename= pathname(filename) + std::string(data->images[i].uri);
705  images[i]= read_image_data(image_filename.c_str());
706  }
707  else if(data->images[i].buffer_view)
708  {
709  // extraire l'image du glb...
710  cgltf_buffer_view *view= data->images[i].buffer_view;
711  assert(view->buffer->data);
712  //~ printf(" [%u] %s offset %lu size %lu, type '%s'\n", i, data->images[i].name, view->offset, view->size, data->images[i].mime_type);
713 
714  SDL_RWops *read= SDL_RWFromConstMem((uint8_t *) view->buffer->data + view->offset, view->size);
715  assert(read);
716 
717  images[i]= image_data( IMG_Load_RW(read, /* free RWops */ 1) );
718  }
719  }
720 
721  cgltf_free(data);
722  return images;
723 }
724 
725 
726 GLTFScene read_gltf_scene( const char *filename )
727 {
728  printf("loading glTF scene '%s'...\n", filename);
729 
730  cgltf_options options= { };
731  cgltf_data *data= nullptr;
732  cgltf_result code= cgltf_parse_file(&options, filename, &data);
733  if(code != cgltf_result_success)
734  {
735  printf("[error] loading glTF mesh '%s'...\n", filename);
736  return { };
737  }
738 
739  if(cgltf_validate(data) != cgltf_result_success)
740  {
741  printf("[error] invalid glTF mesh '%s'...\n", filename);
742  return { };
743  }
744 
745  code= cgltf_load_buffers(&options, data, filename);
746  if(code != cgltf_result_success)
747  {
748  printf("[error] loading glTF buffers...\n");
749  return { };
750  }
751 
752  //
753  GLTFScene scene;
754 
755 // etape 1 : construire les meshs et les groupes de triangles / primitives
756  int primitives_index= 0;
757  std::vector<float> buffer;
758 
759  // parcourir tous les meshs de la scene
760  for(unsigned mesh_id= 0; mesh_id < data->meshes_count; mesh_id++)
761  {
762  GLTFMesh m= { };
763  m.pmin= Point(FLT_MAX, FLT_MAX, FLT_MAX);
764  m.pmax= Point(-FLT_MAX, -FLT_MAX, -FLT_MAX);
765 
766  cgltf_mesh *mesh= &data->meshes[mesh_id];
767  // parcourir les groupes de triangles du mesh...
768  for(unsigned primitive_id= 0; primitive_id < mesh->primitives_count; primitive_id++)
769  {
770  cgltf_primitive *primitives= &mesh->primitives[primitive_id];
771  assert(primitives->type == cgltf_primitive_type_triangles);
772 
773  GLTFPrimitives p= { };
774 
775  // matiere associee au groupe de triangles
776  p.material_index= -1;
777  if(primitives->material)
778  p.material_index= std::distance(data->materials, primitives->material);
779 
780  // indices
781  if(primitives->indices)
782  {
783  for(unsigned i= 0; i < primitives->indices->count; i++)
784  p.indices.push_back(cgltf_accessor_read_index(primitives->indices, i));
785  assert(p.indices.size() % 3 == 0);
786  }
787 
788  // attributs
789  for(unsigned attribute_id= 0; attribute_id < primitives->attributes_count; attribute_id++)
790  {
791  cgltf_attribute *attribute= &primitives->attributes[attribute_id];
792 
793  if(attribute->type == cgltf_attribute_type_position)
794  {
795  assert(attribute->data->type == cgltf_type_vec3);
796 
797  buffer.resize(cgltf_accessor_unpack_floats(attribute->data, nullptr, 0));
798  cgltf_accessor_unpack_floats(attribute->data, buffer.data(), buffer.size());
799 
800  // transforme les positions des sommets
801  for(unsigned i= 0; i+2 < buffer.size(); i+= 3)
802  p.positions.push_back( vec3(buffer[i], buffer[i+1], buffer[i+2]) );
803 
804  #if 0
805  assert(attribute->data->has_min);
806  assert(attribute->data->has_max);
807  p.pmin= vec3(attribute->data->min[0], attribute->data->min[1], attribute->data->min[2]);
808  p.pmax= vec3(attribute->data->max[0], attribute->data->max[1], attribute->data->max[2]);
809  #else
810  p.pmin= p.positions[0];
811  p.pmax= p.positions[0];
812  for(unsigned i= 1; i < p.positions.size(); i++)
813  {
814  p.pmin= min(p.pmin, p.positions[i]);
815  p.pmax= max(p.pmax, p.positions[i]);
816  }
817  #endif
818  m.pmin= min(m.pmin, p.pmin);
819  m.pmax= max(m.pmax, p.pmax);
820  }
821 
822  if(attribute->type == cgltf_attribute_type_normal)
823  {
824  assert(attribute->data->type == cgltf_type_vec3);
825 
826  buffer.resize(cgltf_accessor_unpack_floats(attribute->data, nullptr, 0));
827  cgltf_accessor_unpack_floats(attribute->data, buffer.data(), buffer.size());
828 
829  // transforme les normales des sommets
830  for(unsigned i= 0; i+2 < buffer.size(); i+= 3)
831  p.normals.push_back( vec3(buffer[i], buffer[i+1], buffer[i+2]) );
832  }
833 
834  if(attribute->type == cgltf_attribute_type_texcoord)
835  {
836  assert(attribute->data->type == cgltf_type_vec2);
837 
838  buffer.resize(cgltf_accessor_unpack_floats(attribute->data, nullptr, 0));
839  cgltf_accessor_unpack_floats(attribute->data, buffer.data(), buffer.size());
840 
841  for(unsigned i= 0; i+1 < buffer.size(); i+= 2)
842  p.texcoords.push_back( vec2(buffer[i], buffer[i+1]) );
843  }
844  }
845 
846  p.primitives_index= primitives_index++;
847  m.primitives.push_back(p);
848  }
849 
850  scene.meshes.push_back(m);
851  }
852 
853 // etape 2 : parcourir les noeuds, retrouver les transforms pour placer les meshes
854  for(unsigned node_id= 0; node_id < data->nodes_count; node_id++)
855  {
856  cgltf_node *node= &data->nodes[node_id];
857  if(node->mesh== nullptr)
858  // pas de mesh associe, rien a dessiner
859  continue;
860 
861  // recuperer la transformation pour placer le mesh dans la scene
862  float model_matrix[16];
863  cgltf_node_transform_world(node, model_matrix);
864 
865  Transform model;
866  model.column_major(model_matrix); // gltf organise les 16 floats par colonne...
867  //~ Transform normal= model.normal(); // transformation pour les normales
868 
869  int mesh_index= std::distance(data->meshes, node->mesh);
870  scene.nodes.push_back( {model, mesh_index} );
871  }
872 
873 // etape 3 : recuperer les autres infos...
874  scene.materials= read_materials(data);
875  scene.lights= read_lights(data);
876  scene.cameras= read_cameras(data);
877 
878 // etape : nettoyage...
879  cgltf_free(data);
880 
881  return scene;
882 }
883 
884 std::vector<GLTFInstances> GLTFScene::instances( ) const
885 {
886  std::vector<GLTFInstances> instances(meshes.size());
887  for(unsigned i= 0; i < meshes.size(); i++)
888  instances[i].mesh_index= i;
889 
890  for(unsigned i= 0; i < nodes.size(); i++)
891  {
892  int index= nodes[i].mesh_index;
893  assert(index < int(instances.size()));
894  instances[index].transforms.push_back( nodes[i].model );
895  }
896 
897  return instances;
898 }
899 
900 
901 void GLTFScene::bounds( Point& pmin, Point& pmax ) const
902 {
903  pmin= Point(FLT_MAX, FLT_MAX, FLT_MAX);
904  pmax= Point(-FLT_MAX, -FLT_MAX, -FLT_MAX);
905  for(unsigned node_id= 0; node_id < nodes.size(); node_id++)
906  {
907  const GLTFNode& node= nodes[node_id];
908  const GLTFMesh& mesh= meshes[node.mesh_index];
909  for(unsigned primitive_id= 0; primitive_id < mesh.primitives.size(); primitive_id++)
910  {
911  const GLTFPrimitives& primitives= mesh.primitives[primitive_id];
912  for(unsigned i= 0; i < primitives.positions.size(); i++)
913  {
914  //~ Point p= node.model(Point(primitives.positions[i]));
915  Point p= Point(primitives.positions[i]);
916  pmin= min(pmin, p);
917  pmax= max(pmax, p);
918  }
919  }
920  }
921 }
representation d'un objet / maillage.
Definition: mesh.h:112
unsigned int vertex(const vec3 &p)
insere un sommet de position p, et ses attributs (s'ils sont definis par color(), texcoord(),...
Definition: mesh.cpp:109
Mesh & texcoord(const vec2 &uv)
definit les coordonnees de texture du prochain sommet.
Definition: mesh.cpp:98
static Mesh & error()
Definition: mesh.h:348
Mesh & material(const unsigned int id)
definit la matiere du prochain triangle. id est l'indice d'une matiere ajoutee dans materials(),...
Definition: mesh.cpp:275
Mesh & triangle(const unsigned int a, const unsigned int b, const unsigned int c)
Definition: mesh.cpp:190
Mesh & normal(const vec3 &n)
definit la normale du prochain sommet.
Definition: mesh.cpp:88
GLenum primitives() const
renvoie le type de primitives.
Definition: mesh.h:336
Mesh & color(const vec4 &c)
definit la couleur du prochain sommet.
Definition: mesh.cpp:78
const Materials & materials() const
renvoie la description des matieres.
Definition: mesh.cpp:265
scene glTF.
Point pmax
points extremes de l'englobant dans le repere objet
Definition: gltf.h:117
Mesh read_gltf_mesh(const char *filename)
charge un fichier .gltf et construit un mesh statique, sans animation.
Definition: gltf.cpp:19
int mesh_index
indice du maillage.
Definition: gltf.h:131
std::vector< ImageData > read_gltf_images(const char *filename)
charge un fichier .gltf et charge les images referencees par les matieres.
Definition: gltf.cpp:655
std::vector< GLTFMaterial > read_gltf_materials(const char *filename)
charge un fichier .gltf et renvoie les matieres.
Definition: gltf.cpp:624
int material_index
indice de la matiere des primitives.
Definition: gltf.h:102
int primitives_index
indice unique.
Definition: gltf.h:101
Point pmax
points extremes de l'englobant dans le repere objet
Definition: gltf.h:104
GLTFScene read_gltf_scene(const char *filename)
charge un fichier .gltf et construit une scene statique, sans animation.
Definition: gltf.cpp:726
std::vector< GLTFPrimitives > primitives
groupes de triangles associes a une matiere.
Definition: gltf.h:116
std::vector< GLTFLight > read_gltf_lights(const char *filename)
charge un fichier .gltf et renvoie les sources de lumiere ponctuelles.
Definition: gltf.cpp:467
std::vector< GLTFCamera > read_gltf_cameras(const char *filename)
charge un fichier .gltf renvoie les cameras.
Definition: gltf.cpp:402
description d'un maillage.
Definition: gltf.h:115
position et orientation d'un maillage dans la scene.
Definition: gltf.h:129
groupe de triangles d'un maillage. chaque groupe est associe a une matiere.
Definition: gltf.h:99
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
ImageData read_image_data(const char *filename)
charge les donnees d'un fichier png. renvoie une image initialisee par defaut en cas d'echec.
Definition: image_io.cpp:216
int write_image_data(ImageData &image, const char *filename)
enregistre des donnees dans un fichier png.
Definition: image_io.cpp:229
Color Black()
utilitaire. renvoie une couleur noire.
Definition: color.cpp:31
ImageData image_data(SDL_Surface *surface)
converti une surface SDL en imageData, cf RWops pour charger les images deja en memoire.
Definition: image_io.cpp:141
Image flipY(const Image &image)
retourne l'image
Definition: image_io.cpp:295
Transform Inverse(const Transform &m)
renvoie l'inverse de la matrice.
Definition: mat.cpp:197
Point Origin()
renvoie le point origine (0, 0, 0)
Definition: vec.cpp:8
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
float degrees(const float rad)
conversion en degres.
Definition: mat.cpp:15
float distance(const Point &a, const Point &b)
renvoie la distance etre 2 points.
Definition: vec.cpp:14
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
Transform Perspective(const float fov, const float aspect, const float znear, const float zfar)
renvoie la matrice representant une transformation projection perspective.
Definition: mat.cpp:329
Materials read_materials(const char *filename)
charge une description de matieres, utilise par read_mesh.
Definition: wavefront.cpp:454
void normals(MeshData &data)
(re-) calcule les normales des sommets. utiliser avant les reindexations, cf indices() et vertices().
Definition: mesh_data.cpp:307
std::string pathname(const std::string &filename)
Definition: files.cpp:68
representation d'une couleur (rgba) transparente ou opaque.
Definition: color.h:14
float transmission
transmission, transparent ou pas (= 0)
Definition: gltf.h:64
int color_texture
indice de la texture ou -1. cf read_gltf_images() pour charger les textures dans le bon ordre....
Definition: gltf.h:73
float roughness
rugosite de la micro surface.
Definition: gltf.h:63
int metallic_roughness_texture
indice de la texture ou -1. les valeurs RGB representent les parametres du modele : B= metallic,...
Definition: gltf.h:74
int emission_texture
indice de la texture ou -1.
Definition: gltf.h:75
Color specular_color
modification de la reflexion speculaire des dielectriques ou pas (= 0)
Definition: gltf.h:67
Color emission
emission pour les sources de lumieres ou pas (= noir).
Definition: gltf.h:61
int normal_texture
indice de la texture ou -1.
Definition: gltf.h:77
float specular
modification de la reflexion speculaire des dielectriques ou pas (= 0)
Definition: gltf.h:66
int transmission_texture
indice de la texture ou -1.
Definition: gltf.h:78
float ior
indice de refraction des dielectriques ou pas (= 0)
Definition: gltf.h:65
float thickness
epaisseur des surfaces transparentes
Definition: gltf.h:68
int specular_color_texture
indice de la texture ou -1.
Definition: gltf.h:80
float metallic
metallic / dielectrique.
Definition: gltf.h:62
int specular_texture
indice de la texture ou -1.
Definition: gltf.h:79
Color color
base color.
Definition: gltf.h:60
int occlusion_texture
indice de la texture ou -1. //
Definition: gltf.h:76
void bounds(Point &pmin, Point &pmax) const
calcule les points extremes de la scene, utile pour regler un orbiter.
Definition: gltf.cpp:901
std::vector< GLTFMaterial > materials
matieres.
Definition: gltf.h:153
std::vector< GLTFLight > lights
lumieres.
Definition: gltf.h:154
std::vector< GLTFInstances > instances() const
regroupe les instances de chaque maillage.
Definition: gltf.cpp:884
std::vector< GLTFNode > nodes
noeuds / position et orientation des maillages dans la scene.
Definition: gltf.h:151
std::vector< GLTFMesh > meshes
ensemble de maillages.
Definition: gltf.h:150
std::vector< GLTFCamera > cameras
cameras.
Definition: gltf.h:155
stockage temporaire des donnees d'une image.
Definition: image_io.h:38
int diffuse_texture
indice de la texture de la couleur de base, ou -1.
Definition: materials.h:21
float ns
concentration des reflets, exposant pour les reflets blinn-phong.
Definition: materials.h:20
Color diffuse
couleur diffuse / de base.
Definition: materials.h:17
Color specular
couleur du reflet.
Definition: materials.h:18
int insert(const Material &material, const char *name)
ajoute une matiere.
Definition: materials.h:54
int find(const char *name)
recherche une matiere avec son nom. renvoie son indice dans materials, ou -1.
Definition: materials.h:80
int insert_texture(const char *filename)
ajoute une texture / nom du fichier.
Definition: materials.h:68
int count() const
nombre de matieres.
Definition: materials.h:94
representation d'un point 3d.
Definition: vec.h:21
representation d'une transformation, une matrice 4x4, organisee par ligne / row major.
Definition: mat.h:21
Transform & column_major(const float matrix[16])
initialise la matrice avec 16 floats organises par colonne.
Definition: mat.cpp:76
Transform normal() const
renvoie la transformation a appliquer aux normales d'un objet transforme par la matrice m.
Definition: mat.cpp:181
representation d'un vecteur 3d.
Definition: vec.h:59
vecteur generique, utilitaire.
Definition: vec.h:131
vecteur generique, utilitaire.
Definition: vec.h:146
vecteur generique 4d, ou 3d homogene, utilitaire.
Definition: vec.h:168
bool has_normals(const Hit &hit, const GLTFScene &scene)
verifie la presence des normales par sommet.
bool has_texcoords(const Hit &hit, const GLTFScene &scene)
verifie la presence des coordonnees de texture...