gKit2 light
shader_kit.cpp
Go to the documentation of this file.
1 
3 
4 #include <cstdio>
5 #include <cstring>
6 
7 #include <chrono>
8 
9 #include "glcore.h"
10 #include "window.h"
11 #include "files.h"
12 
13 #include "program.h"
14 #include "uniforms.h"
15 
16 #include "texture.h"
17 #include "mesh.h"
18 #include "wavefront.h"
19 
20 #include "vec.h"
21 #include "mat.h"
22 #include "orbiter.h"
23 
24 #include "color.h"
25 #include "image.h"
26 #include "image_hdr.h"
27 
28 #include "text.h"
29 #include "widgets.h"
30 
31 
32 
33 // program
34 const char *program_filename;
35 GLuint program;
36 
37 // affichage des erreurs
38 std::string program_log;
39 int program_area;
40 bool program_failed;
41 
42 const char *mesh_filename;
43 Mesh mesh;
44 Point mesh_pmin;
45 Point mesh_pmax;
46 int vertex_count;
47 GLuint vao;
48 bool wireframe= false;
49 
50 std::vector<const char *> texture_filenames;
51 std::vector<GLuint> textures;
52 
53 Orbiter camera;
54 Widgets widgets;
55 
56 // application
57 size_t last_load= 0;
58 void reload_program( )
59 {
60  if(program == 0)
61  program= read_program(program_filename);
62  else
63  reload_program(program, program_filename);
64 
65  // conserve la date du fichier
66  last_load= timestamp(program_filename);
67 
68  // recupere les erreurs, si necessaire
69  program_area= program_format_errors(program, program_log);
70 
71  if(program_log.size() > 0)
72  printf("[boom]\n%s\n", program_log.c_str());
73 
74  program_failed= (program_log.size() > 0);
75 }
76 
77 
78 // cherche un fichier avec l'extension ext dans les options
79 const char *option_find( std::vector<const char *>& options, const char *ext )
80 {
81  for(unsigned int i= 0; i < (unsigned int) options.size() ; i++)
82  {
83  if(options[i] != nullptr && std::string(options[i]).rfind(ext) != std::string::npos)
84  {
85  const char *option= options[i];
86  options[i]= nullptr;
87  return option;
88  }
89  }
90 
91  return nullptr;
92 }
93 
96 int init( std::vector<const char *>& options )
97 {
98  widgets= create_widgets();
99  camera= Orbiter();
100 
101  program= 0;
102  const char *option;
103  option= option_find(options, ".glsl");
104  if(option != nullptr)
105  {
106  program_filename= option;
107  reload_program();
108  }
109 
110  vao= 0;
111  mesh_pmin= Point(normalize(Vector(-1, -1, 0)) * 2.5f);
112  mesh_pmax= Point(normalize(Vector( 1, 1, 0)) * 2.5f);
113 
114  option= option_find(options, ".obj");
115  if(option != nullptr)
116  {
117  mesh= read_mesh(option);
118  if(mesh.vertex_count() > 0)
119  {
120  mesh_filename= option;
121 
122  vao= mesh.create_buffers(mesh.has_texcoord(), mesh.has_normal(), mesh.has_color(), mesh.has_material_index());
123  vertex_count= mesh.vertex_count();
124 
125  mesh.bounds(mesh_pmin, mesh_pmax);
126  camera.lookat(mesh_pmin, mesh_pmax);
127  }
128 
129  // ou generer une erreur ?
130  }
131 
132  if(vao == 0)
133  {
134  glGenVertexArrays(1, &vao);
135  vertex_count= 3;
136  }
137 
138  // charge les textures, si necessaire
139  for(int i= 0; i < int(options.size()); i++)
140  {
141  if(options[i] == nullptr)
142  continue;
143 
144  GLuint texture= read_texture(0, options[i]);
145  if(texture > 0)
146  {
147  texture_filenames.push_back(options[i]);
148  textures.push_back(texture);
149  }
150  }
151 
152  // nettoyage
153  glUseProgram(0);
154  glBindTexture(GL_TEXTURE_2D, 0);
155  glBindVertexArray(0);
156  glBindBuffer(GL_ARRAY_BUFFER, 0);
157  glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
158 
159  // etat openGL par defaut
160  glClearColor(0.2f, 0.2f, 0.2f, 1.f);
161  glClearDepth(1.f);
162 
163  glCullFace(GL_BACK);
164  glFrontFace(GL_CCW);
165  //~ glEnable(GL_CULL_FACE); // n'affiche que les faces correctement orientees...
166  glDisable(GL_CULL_FACE); // les faces mal orientees sont affichees avec des hachures oranges...
167 
168  glDepthFunc(GL_LESS);
169  glEnable(GL_DEPTH_TEST);
170 
171  return 0;
172 }
173 
174 int quit( )
175 {
176  // detruit les objets openGL
177  release_widgets(widgets);
178  release_program(program);
179  mesh.release();
180 
181  glDeleteTextures(textures.size(), textures.data());
182  return 0;
183 }
184 
185 int draw( void )
186 {
187  if(wireframe)
188  {
189  glClearColor(1, 1, 1, 1);
190  glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
191  glLineWidth(2);
192  }
193  else
194  {
195  glClearColor(0.2f, 0.2f, 0.2f, 1);
196  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
197  }
198 
199  // effacer l'image
200  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
201 
202  // verification de la date du fichier source des shaders...
203  static float last_time= 0;
204  // toutes les secondes, ca suffit, pas tres malin de le faire 60 fois par seconde...
205  if(global_time() > last_time + 400)
206  {
207  if(timestamp(program_filename) != last_load)
208  // date modifiee, recharger les sources et recompiler...
209  reload_program();
210 
211  // attends le chargement et la compilation des shaders... au cas ou ce soit plus long qu'une seconde...
212  // (oui ca arrive...)
213  last_time= global_time();
214  }
215 
216  if(key_state('r'))
217  {
218  clear_key_state('r');
219  reload_program();
220  }
221 
222  // recupere les mouvements de la souris
223  int mx, my;
224  unsigned int mb= SDL_GetRelativeMouseState(&mx, &my);
225  int mousex, mousey;
226  SDL_GetMouseState(&mousex, &mousey);
227 
228  // deplace la camera
229  if(mb & SDL_BUTTON(1))
230  camera.rotation(mx, my); // tourne autour de l'objet
231  else if(mb & SDL_BUTTON(3))
232  camera.translation((float) mx / (float) window_width(), (float) my / (float) window_height()); // deplace le point de rotation
233  else if(mb & SDL_BUTTON(2))
234  camera.move(mx); // approche / eloigne l'objet
235 
236  SDL_MouseWheelEvent wheel= wheel_event();
237  if(wheel.y != 0)
238  {
240  camera.move(8.f * wheel.y); // approche / eloigne l'objet
241  }
242 
243  // etat
244  static float time= 0;
245  static int frame= 0;
246  static int zbuffer= 0;
247  static int video= 0;
248  static int freeze= 0;
249  static int reset_camera= 0;
250  static int copy_camera= 0;
251  static int paste_camera= 0;
252 
253  // recupere les transformations
254  Transform model= Identity();
255  Transform view= camera.view();
256  Transform projection= camera.projection(window_width(), window_height(), 45);
258 
259  Transform mvp= projection * view * model;
260  Transform mvpInv= Inverse(mvp);
261  Transform mv= model * view;
262 
263  // temps
264  if(key_state('t'))
265  {
266  clear_key_state('t');
267  freeze= (freeze+1) % 2;
268  }
269  if(freeze == 0)
270  time= global_time();
271 
272  // affiche l'objet
273  if(program_failed == false)
274  {
275  if(key_state('w'))
276  {
277  clear_key_state('w');
278  wireframe= !wireframe;
279  }
280 
281  // configuration minimale du pipeline
282  glBindVertexArray(vao);
283  glUseProgram(program);
284 
285  // affecte une valeur aux uniforms
286  // transformations standards
287  program_uniform(program, "modelMatrix", model);
288  program_uniform(program, "modelInvMatrix", model.inverse());
289  program_uniform(program, "viewMatrix", view);
290  program_uniform(program, "viewInvMatrix", view.inverse());
291  program_uniform(program, "projectionMatrix", projection);
292  program_uniform(program, "projectionInvMatrix", projection.inverse());
293  program_uniform(program, "viewportMatrix", viewport);
294  program_uniform(program, "viewportInvMatrix", viewport.inverse());
295 
296  program_uniform(program, "mvpMatrix", mvp);
297  program_uniform(program, "mvpInvMatrix", mvpInv);
298 
299  program_uniform(program, "mvMatrix", mv);
300  program_uniform(program, "mvInvMatrix", mv.inverse());
301  program_uniform(program, "normalMatrix", mv.normal());
302 
303  // interactions
304  program_uniform(program, "viewport", vec2(window_width(), window_height()));
305  program_uniform(program, "time", time);
306  program_uniform(program, "motion", vec3(mx, my, mb & SDL_BUTTON(1)));
307  program_uniform(program, "mouse", vec3(mousex, window_height() - mousey -1, mb & SDL_BUTTON(1)));
308 
309  // textures
310  for(unsigned i= 0; i < textures.size(); i++)
311  {
312  char uniform[1024];
313  sprintf(uniform, "texture%u", i);
314  program_use_texture(program, uniform, i, textures[i]);
315  }
316 
317  // go
318  glDrawArrays(GL_TRIANGLES, 0, vertex_count);
319  }
320 
321 
322  // affiche les infos...
323  begin(widgets);
324  if(program_failed)
325  {
326  label(widgets, "[error] program '%s'", program_filename);
327  begin_line(widgets);
328  text_area(widgets, 20, program_log.c_str(), program_area);
329  }
330  else
331  {
332  button(widgets, "[s] screenshot ", frame);
333  button(widgets, "[z] depth screenshot", zbuffer);
334  button(widgets, "capture frames", video);
335  begin_line(widgets);
336  button(widgets, "[t] freeze time", freeze);
337  button(widgets, "[f] reset camera", reset_camera);
338  button(widgets, "[c] copy/save camera", copy_camera);
339  button(widgets, "[v] paste/read camera", paste_camera);
340 
341  begin_line(widgets);
342  begin_line(widgets);
343  label(widgets, "program '%s' running...", program_filename);
344  if(mesh_filename && mesh_filename[0])
345  {
346  begin_line(widgets);
347  label(widgets, "mesh '%s', %d vertices %s %s", mesh_filename, mesh.vertex_count(),
348  mesh.texcoord_buffer_size() ? "texcoords" : "", mesh.normal_buffer_size() ? "normals" : "");
349  }
350  for(unsigned i= 0; i < texture_filenames.size(); i++)
351  {
352  begin_line(widgets);
353  label(widgets, "texture%u '%s'", i, texture_filenames[i]);
354  }
355 
356  }
357 
358  end(widgets);
359 
360 
361  if(zbuffer || key_state('z'))
362  {
363  zbuffer= 0;
364  clear_key_state('z');
365 
366  frame= 1; // force un screenshot en meme temps
367  copy_camera= 1; // idem pour l'orbiter
368 
369  printf("zbuffer screenshot...\n");
370 
371  // recupere le zbuffer
372  Image image(window_width(), window_height());
373  std::vector<float> tmp(image.width() * image.height());
374 
375  glReadBuffer(GL_BACK);
376  glReadPixels(0, 0, image.width(), image.height(), GL_DEPTH_COMPONENT, GL_FLOAT, tmp.data());
377 
378  // conversion en image
379  for(unsigned i= 0; i < image.size(); i++)
380  image(i)= Color(tmp[i]);
381 
382  write_image_pfm(image, "zbuffer_viewport.pfm");
383 
384  // transformation dans le repere camera
385  Transform inv= Inverse( viewport * projection );
386  for(int py= 0; py < image.height(); py++)
387  for(int px= 0; px < image.width(); px++)
388  {
389  int i= py * image.width() + px;
390  Point fragment= Point(float(px) + float(0.5), float(py) + float(0.5), tmp[i]);
391  Point p= inv( fragment );
392 
393  image(i)= Color(p.z);
394  }
395 
396  write_image_pfm(image, "zbuffer_camera.pfm");
397 
398  // exporte une image avec un z positif...
399  for(unsigned i= 0; i < image.size(); i++)
400  image(i)= Color(std::abs(image(i).r));
401 
402  write_image_pfm(image, "zbuffer_color.pfm");
403  }
404 
405  //
406  draw(widgets, window_width(), window_height());
407 
408  if(frame || key_state('s'))
409  {
410  frame= 0;
411  clear_key_state('s');
412 
413  static int calls= 1;
414  printf("screenshot %d...\n", calls);
415  screenshot("shader_kit", calls++);
416  }
417 
418  if(video)
419  capture("shader_kit");
420 
421  if(copy_camera || key_state('c'))
422  {
423  copy_camera= 0;
424  clear_key_state('c');
425  camera.write_orbiter("orbiter.txt");
426  }
427  if(paste_camera || key_state('v'))
428  {
429  paste_camera= 0;
430  clear_key_state('v');
431  if(camera.read_orbiter("orbiter.txt") < 0)
432  {
433  camera= Orbiter();
434  camera.lookat(mesh_pmin, mesh_pmax);
435  }
436  }
437 
438  if(reset_camera || key_state('f'))
439  {
440  reset_camera= 0;
441  clear_key_state('f');
442 
443  camera= Orbiter();
444  camera.lookat(mesh_pmin, mesh_pmax);
445  }
446 
447  return 1;
448 }
449 
450 
451 int main( int argc, char **argv )
452 {
453  if(argc == 1)
454  {
455  printf("usage: %s shader.glsl [mesh.obj] [texture0.png [texture1.png]]\n", argv[0]);
456  return 0;
457  }
458 
459  Window window= create_window(1024, 640);
460  if(window == nullptr)
461  return 1;
462 
463  Context context= create_context(window);
464  if(context == nullptr)
465  return 1;
466 
467  // creation des objets opengl
468  std::vector<const char *> options(argv + 1, argv + argc);
469  if(init(options) < 0)
470  {
471  printf("[error] init failed.\n");
472  return 1;
473  }
474 
475  // affichage de l'application
476  run(window, draw);
477 
478  // nettoyage
479  quit();
480  release_context(context);
481  release_window(window);
482  return 0;
483 }
representation d'une image.
Definition: image.h:21
representation d'un objet / maillage.
Definition: mesh.h:112
std::size_t texcoord_buffer_size() const
renvoie la taille (en octets) du texcoord buffer.
Definition: mesh.h:308
GLuint create_buffers(const bool use_texcoord, const bool use_normal, const bool use_color, const bool use_material_index)
construit les buffers et le vertex array object necessaires pour dessiner l'objet avec openGL....
Definition: mesh.cpp:581
void bounds(Point &pmin, Point &pmax) const
renvoie min et max les coordonnees des extremites des positions des sommets de l'objet (boite engloba...
Definition: mesh.cpp:503
int vertex_count() const
renvoie le nombre de sommets.
Definition: mesh.h:291
void release()
detruit les objets openGL.
Definition: mesh.cpp:64
std::size_t normal_buffer_size() const
renvoie la longueur (en octets) du normal buffer.
Definition: mesh.h:303
representation de la camera, type orbiter, placee sur une sphere autour du centre de l'objet.
Definition: orbiter.h:17
void lookat(const Point &center, const float size)
observe le point center a une distance size.
Definition: orbiter.cpp:7
int read_orbiter(const char *filename)
relit la position de l'orbiter depuis un fichier texte.
Definition: orbiter.cpp:116
void move(const float z)
rapproche / eloigne la camera du centre.
Definition: orbiter.cpp:33
Transform projection(const int width, const int height, const float fov)
fixe la projection reglee pour une image d'aspect width / height, et une demi ouverture de fov degres...
Definition: orbiter.cpp:47
void translation(const float x, const float y)
deplace le centre / le point observe.
Definition: orbiter.cpp:27
void rotation(const float x, const float y)
change le point de vue / la direction d'observation.
Definition: orbiter.cpp:21
int write_orbiter(const char *filename)
enregistre la position de l'orbiter dans un fichier texte.
Definition: orbiter.cpp:150
Transform view() const
renvoie la transformation vue.
Definition: orbiter.cpp:40
void begin(Widgets &w)
debut de la description des elements de l'interface graphique.
Definition: widgets.cpp:29
Widgets create_widgets()
cree une interface graphique. a detruire avec release_widgets( ).
Definition: widgets.cpp:12
void release_widgets(Widgets &w)
detruit l'interface graphique.
Definition: widgets.cpp:23
Context create_context(Window window)
cree et configure un contexte opengl
Definition: window.cpp:356
bool button(Widgets &w, const char *text, int &status)
Definition: widgets.cpp:155
void label(Widgets &w, const char *format,...)
cree un texte. meme fonctionnement que printf().
Definition: widgets.cpp:142
int window_height()
renvoie la hauteur de la fenetre de l'application.
Definition: window.cpp:29
void release_window(Window window)
destruction de la fenetre.
Definition: window.cpp:325
void clear_key_state(const SDL_Keycode key)
desactive une touche du clavier.
Definition: window.cpp:48
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
Window create_window(const int w, const int h, const int major, const int minor, const int samples)
creation d'une fenetre pour l'application.
Definition: window.cpp:259
void release_context(Context context)
detruit le contexte openGL.
Definition: window.cpp:422
void clear_wheel_event()
desactive l'evenement.
Definition: window.cpp:116
void end(Widgets &w)
termine la description des elements de l'interface graphique.
Definition: widgets.cpp:404
int key_state(const SDL_Keycode key)
renvoie l'etat d'une touche du clavier. cf la doc SDL2 pour les codes.
Definition: window.cpp:42
void text_area(Widgets &w, const int height, const char *text, int &begin_line)
Definition: widgets.cpp:273
void begin_line(Widgets &w)
place les prochains elements sur une nouvelle ligne.
Definition: widgets.cpp:129
SDL_MouseWheelEvent wheel_event()
renvoie le dernier evenement. etat de la molette de la souris / glisser sur le pad.
Definition: window.cpp:112
int window_width()
renvoie la largeur de la fenetre de l'application.
Definition: window.cpp:25
float global_time()
renvoie le temps ecoule depuis le lancement de l'application, en millisecondes.
Definition: window.cpp:128
Transform Inverse(const Transform &m)
renvoie l'inverse de la matrice.
Definition: mat.cpp:197
Transform Viewport(const float width, const float height)
renvoie la matrice representant une transformation viewport.
Definition: mat.cpp:357
Transform Identity()
construit la transformation identite.
Definition: mat.cpp:187
Vector normalize(const Vector &v)
renvoie un vecteur unitaire / longueur == 1.
Definition: vec.cpp:123
Mesh read_mesh(const char *filename)
charge un fichier wavefront .obj et renvoie un mesh compose de triangles non indexes....
Definition: wavefront.cpp:14
int capture(const char *prefix)
Definition: texture.cpp:215
int screenshot(const char *filename)
enregistre le contenu de la fenetre dans un fichier. doit etre de type .png / .bmp
Definition: texture.cpp:188
GLuint read_program(const char *filename, const char *definitions)
Definition: program.cpp:204
void program_uniform(const GLuint program, const char *uniform, const std::vector< unsigned > &v)
affecte un tableau de valeurs a un uniform du shader program.
Definition: uniforms.cpp:94
int release_program(const GLuint program)
detruit les shaders et le program.
Definition: program.cpp:211
void program_use_texture(const GLuint program, const char *uniform, const int unit, const GLuint texture, const GLuint sampler)
configure le pipeline et le shader program pour utiliser une texture, et des parametres de filtrage,...
Definition: uniforms.cpp:198
GLuint read_texture(const int unit, const char *filename, const GLenum texel_type)
Definition: texture.cpp:148
int program_format_errors(const GLuint program, std::string &errors)
renvoie les erreurs de compilation.
Definition: program.cpp:366
int write_image_pfm(const Image &image, const char *filename)
enregistre une image dans un fichier .pfm.
Definition: image_hdr.cpp:146
int init(std::vector< const char * > &options)
Definition: shader_kit.cpp:96
representation d'une couleur (rgba) transparente ou opaque.
Definition: color.h:14
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 normal() const
renvoie la transformation a appliquer aux normales d'un objet transforme par la matrice m.
Definition: mat.cpp:181
Transform inverse() const
renvoie l'inverse de la matrice.
Definition: mat.cpp:399
representation d'un vecteur 3d.
Definition: vec.h:59
vecteur generique, utilitaire.
Definition: vec.h:131
vecteur generique, utilitaire.
Definition: vec.h:146
int run(Window window, int(*draw)())
boucle de gestion des evenements de l'application.
Definition: window.cpp:147