progs/glsl: new shtest program, a simple shader test harness app
[mesa.git] / progs / glsl / shtest.c
1 /*
2 * Simple shader test harness.
3 * Brian Paul
4 * 13 Aug 2009
5 *
6 * Usage:
7 * shtest --vs vertShaderFile --fs fragShaderFile
8 *
9 * In this case the given vertex/frag shaders are read and compiled.
10 * Random values are assigned to the uniforms.
11 *
12 * or:
13 * shtest configFile
14 *
15 * In this case a config file is read that specifies the file names
16 * of the shaders plus initial values for uniforms.
17 *
18 * Example config file:
19 *
20 * vs shader.vert
21 * fs shader.frag
22 * uniform pi 3.14159
23 * uniform v1 1.0 0.5 0.2 0.3
24 *
25 */
26
27
28 #include <assert.h>
29 #include <string.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <math.h>
34 #include <GL/glew.h>
35 #include <GL/glu.h>
36 #include <GL/glut.h>
37 #include "shaderutil.h"
38
39
40 typedef enum
41 {
42 SPHERE,
43 CUBE,
44 NUM_SHAPES
45 } shape;
46
47
48 static char *FragShaderFile = NULL;
49 static char *VertShaderFile = NULL;
50 static char *ConfigFile = NULL;
51
52 /* program/shader objects */
53 static GLuint fragShader;
54 static GLuint vertShader;
55 static GLuint Program;
56
57
58 #define MAX_UNIFORMS 100
59 static struct uniform_info Uniforms[MAX_UNIFORMS];
60 static GLuint NumUniforms = 0;
61
62
63 #define MAX_ATTRIBS 100
64 static struct attrib_info Attribs[MAX_ATTRIBS];
65 static GLuint NumAttribs = 0;
66
67
68 /**
69 * Config file info.
70 */
71 struct config_file
72 {
73 struct name_value
74 {
75 char name[100];
76 float value[4];
77 int type;
78 } uniforms[100];
79
80 int num_uniforms;
81 };
82
83
84 static GLint win = 0;
85 static GLboolean Anim = GL_FALSE;
86 static GLfloat TexRot = 0.0;
87 static GLfloat xRot = 0.0f, yRot = 0.0f, zRot = 0.0f;
88 static shape Object = SPHERE;
89
90
91 static float
92 RandomFloat(float min, float max)
93 {
94 int k = rand() % 10000;
95 float x = min + (max - min) * k / 10000.0;
96 return x;
97 }
98
99
100 /** Set new random values for uniforms */
101 static void
102 RandomUniformValues(void)
103 {
104 GLuint i;
105 for (i = 0; i < NumUniforms; i++) {
106 if (Uniforms[i].type == GL_FLOAT) {
107 Uniforms[i].value[0] = RandomFloat(0.0, 1.0);
108 }
109 else {
110 Uniforms[i].value[0] = RandomFloat(-1.0, 2.0);
111 Uniforms[i].value[1] = RandomFloat(-1.0, 2.0);
112 Uniforms[i].value[2] = RandomFloat(-1.0, 2.0);
113 Uniforms[i].value[3] = RandomFloat(-1.0, 2.0);
114 }
115 }
116 }
117
118
119 static void
120 Idle(void)
121 {
122 yRot += 2.0;
123 if (yRot > 360.0)
124 yRot -= 360.0;
125 glutPostRedisplay();
126 }
127
128
129
130 static void
131 SquareVertex(GLfloat s, GLfloat t, GLfloat size)
132 {
133 GLfloat x = -size + s * 2.0 * size;
134 GLfloat y = -size + t * 2.0 * size;
135 glTexCoord2f(s, t);
136 glVertex2f(x, y);
137 }
138
139
140 /*
141 * Draw a square, specifying normal and tangent vectors.
142 */
143 static void
144 Square(GLfloat size)
145 {
146 GLint tangentAttrib = 1;
147 glNormal3f(0, 0, 1);
148 glVertexAttrib3f(tangentAttrib, 1, 0, 0);
149 glBegin(GL_POLYGON);
150 #if 0
151 SquareVertex(0, 0, size);
152 SquareVertex(1, 0, size);
153 SquareVertex(1, 1, size);
154 SquareVertex(0, 1, size);
155 #else
156 glTexCoord2f(0, 0); glVertex2f(-size, -size);
157 glTexCoord2f(1, 0); glVertex2f( size, -size);
158 glTexCoord2f(1, 1); glVertex2f( size, size);
159 glTexCoord2f(0, 1); glVertex2f(-size, size);
160 #endif
161 glEnd();
162 }
163
164
165 static void
166 Cube(GLfloat size)
167 {
168 /* +X */
169 glPushMatrix();
170 glRotatef(90, 0, 1, 0);
171 glTranslatef(0, 0, size);
172 Square(size);
173 glPopMatrix();
174
175 /* -X */
176 glPushMatrix();
177 glRotatef(-90, 0, 1, 0);
178 glTranslatef(0, 0, size);
179 Square(size);
180 glPopMatrix();
181
182 /* +Y */
183 glPushMatrix();
184 glRotatef(90, 1, 0, 0);
185 glTranslatef(0, 0, size);
186 Square(size);
187 glPopMatrix();
188
189 /* -Y */
190 glPushMatrix();
191 glRotatef(-90, 1, 0, 0);
192 glTranslatef(0, 0, size);
193 Square(size);
194 glPopMatrix();
195
196
197 /* +Z */
198 glPushMatrix();
199 glTranslatef(0, 0, size);
200 Square(size);
201 glPopMatrix();
202
203 /* -Z */
204 glPushMatrix();
205 glRotatef(180, 0, 1, 0);
206 glTranslatef(0, 0, size);
207 Square(size);
208 glPopMatrix();
209 }
210
211
212 static void
213 Sphere(GLfloat radius, GLint slices, GLint stacks)
214 {
215 static GLUquadricObj *q = NULL;
216
217 if (!q) {
218 q = gluNewQuadric();
219 gluQuadricDrawStyle(q, GLU_FILL);
220 gluQuadricNormals(q, GLU_SMOOTH);
221 gluQuadricTexture(q, GL_TRUE);
222 }
223
224 gluSphere(q, radius, slices, stacks);
225 }
226
227
228 static void
229 Redisplay(void)
230 {
231 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
232
233 glPushMatrix();
234 glRotatef(xRot, 1.0f, 0.0f, 0.0f);
235 glRotatef(yRot, 0.0f, 1.0f, 0.0f);
236 glRotatef(zRot, 0.0f, 0.0f, 1.0f);
237
238 glMatrixMode(GL_TEXTURE);
239 glLoadIdentity();
240 glRotatef(TexRot, 0.0f, 1.0f, 0.0f);
241 glMatrixMode(GL_MODELVIEW);
242
243 if (Object == SPHERE) {
244 Sphere(2.0, 20, 10);
245 }
246 else if (Object == CUBE) {
247 Cube(2.0);
248 }
249
250 glPopMatrix();
251
252 glutSwapBuffers();
253 }
254
255
256 static void
257 Reshape(int width, int height)
258 {
259 glViewport(0, 0, width, height);
260 glMatrixMode(GL_PROJECTION);
261 glLoadIdentity();
262 glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 25.0);
263 glMatrixMode(GL_MODELVIEW);
264 glLoadIdentity();
265 glTranslatef(0.0f, 0.0f, -15.0f);
266 }
267
268
269 static void
270 CleanUp(void)
271 {
272 glDeleteShader(fragShader);
273 glDeleteShader(vertShader);
274 glDeleteProgram(Program);
275 glutDestroyWindow(win);
276 }
277
278
279 static void
280 Key(unsigned char key, int x, int y)
281 {
282 const GLfloat step = 2.0;
283 (void) x;
284 (void) y;
285
286 switch(key) {
287 case 'a':
288 Anim = !Anim;
289 if (Anim)
290 glutIdleFunc(Idle);
291 else
292 glutIdleFunc(NULL);
293 break;
294 case 'z':
295 zRot += step;
296 break;
297 case 'Z':
298 zRot -= step;
299 break;
300 case 'o':
301 Object = (Object + 1) % NUM_SHAPES;
302 break;
303 case 'r':
304 RandomUniformValues();
305 SetUniformValues(Program, Uniforms);
306 PrintUniforms(Uniforms);
307 break;
308 case 27:
309 CleanUp();
310 exit(0);
311 break;
312 }
313 glutPostRedisplay();
314 }
315
316
317 static void
318 SpecialKey(int key, int x, int y)
319 {
320 const GLfloat step = 2.0;
321
322 (void) x;
323 (void) y;
324
325 switch(key) {
326 case GLUT_KEY_UP:
327 xRot += step;
328 break;
329 case GLUT_KEY_DOWN:
330 xRot -= step;
331 break;
332 case GLUT_KEY_LEFT:
333 yRot -= step;
334 break;
335 case GLUT_KEY_RIGHT:
336 yRot += step;
337 break;
338 }
339 glutPostRedisplay();
340 }
341
342
343 static void
344 InitUniforms(const struct config_file *conf,
345 struct uniform_info uniforms[])
346 {
347 int i;
348
349 for (i = 0; i < conf->num_uniforms; i++) {
350 int j;
351 for (j = 0; uniforms[j].name; j++) {
352 if (strcmp(uniforms[j].name, conf->uniforms[i].name) == 0) {
353 uniforms[j].type = conf->uniforms[i].type;
354 uniforms[j].value[0] = conf->uniforms[i].value[0];
355 uniforms[j].value[1] = conf->uniforms[i].value[1];
356 uniforms[j].value[2] = conf->uniforms[i].value[2];
357 uniforms[j].value[3] = conf->uniforms[i].value[3];
358 }
359 }
360 }
361 }
362
363
364 /**
365 * Read a config file.
366 */
367 static void
368 ReadConfigFile(const char *filename, struct config_file *conf)
369 {
370 char line[1000];
371 FILE *f;
372
373 f = fopen(filename, "r");
374 if (!f) {
375 fprintf(stderr, "Unable to open config file %s\n", filename);
376 exit(1);
377 }
378
379 conf->num_uniforms = 0;
380
381 /* ugly but functional parser */
382 while (!feof(f)) {
383 fgets(line, sizeof(line), f);
384 if (line[0]) {
385 if (strncmp(line, "vs ", 3) == 0) {
386 VertShaderFile = strdup(line + 3);
387 VertShaderFile[strlen(VertShaderFile) - 1] = 0;
388 }
389 else if (strncmp(line, "fs ", 3) == 0) {
390 FragShaderFile = strdup(line + 3);
391 FragShaderFile[strlen(FragShaderFile) - 1] = 0;
392 }
393 else if (strncmp(line, "uniform ", 8) == 0) {
394 char name[1000];
395 int k;
396 float v1, v2, v3, v4;
397 GLenum type;
398
399 k = sscanf(line + 8, "%s %f %f %f %f", name, &v1, &v2, &v3, &v4);
400
401 switch (k) {
402 case 1:
403 type = GL_NONE;
404 abort();
405 break;
406 case 2:
407 type = GL_FLOAT;
408 break;
409 case 3:
410 type = GL_FLOAT_VEC2;
411 break;
412 case 4:
413 type = GL_FLOAT_VEC3;
414 break;
415 case 5:
416 type = GL_FLOAT_VEC4;
417 break;
418 }
419
420 strcpy(conf->uniforms[conf->num_uniforms].name, name);
421 conf->uniforms[conf->num_uniforms].value[0] = v1;
422 conf->uniforms[conf->num_uniforms].value[1] = v2;
423 conf->uniforms[conf->num_uniforms].value[2] = v3;
424 conf->uniforms[conf->num_uniforms].value[3] = v4;
425 conf->uniforms[conf->num_uniforms].type = type;
426 conf->num_uniforms++;
427 }
428 else {
429 if (strlen(line) > 1) {
430 fprintf(stderr, "syntax error in: %s\n", line);
431 break;
432 }
433 }
434 }
435 }
436
437 fclose(f);
438 }
439
440
441 static void
442 Init(void)
443 {
444 struct config_file config;
445 memset(&config, 0, sizeof(config));
446
447 if (ConfigFile)
448 ReadConfigFile(ConfigFile, &config);
449
450 if (!VertShaderFile) {
451 fprintf(stderr, "Error: no vertex shader\n");
452 exit(1);
453 }
454
455 if (!FragShaderFile) {
456 fprintf(stderr, "Error: no fragment shader\n");
457 exit(1);
458 }
459
460 if (!ShadersSupported())
461 exit(1);
462
463 vertShader = CompileShaderFile(GL_VERTEX_SHADER, VertShaderFile);
464 fragShader = CompileShaderFile(GL_FRAGMENT_SHADER, FragShaderFile);
465 Program = LinkShaders(vertShader, fragShader);
466
467 glUseProgram(Program);
468
469 NumUniforms = GetUniforms(Program, Uniforms);
470 if (config.num_uniforms) {
471 InitUniforms(&config, Uniforms);
472 }
473 else {
474 RandomUniformValues();
475 }
476 SetUniformValues(Program, Uniforms);
477 PrintUniforms(Uniforms);
478
479 NumAttribs = GetAttribs(Program, Attribs);
480 PrintAttribs(Attribs);
481
482 //assert(glGetError() == 0);
483
484 glClearColor(0.4f, 0.4f, 0.8f, 0.0f);
485
486 glEnable(GL_DEPTH_TEST);
487
488 glColor3f(1, 0, 0);
489 }
490
491
492 static void
493 Keys(void)
494 {
495 printf("Keyboard:\n");
496 printf(" a Animation toggle\n");
497 printf(" r Randomize uniform values\n");
498 printf(" o Change object\n");
499 printf(" arrows Rotate object\n");
500 printf(" ESC Exit\n");
501 }
502
503
504 static void
505 Usage(void)
506 {
507 printf("Usage:\n");
508 printf(" shtest config.shtest\n");
509 printf(" Run w/ given config file.\n");
510 printf(" shtest --vs vertShader --fs fragShader\n");
511 printf(" Load/compile given shaders.\n");
512 }
513
514
515 static void
516 ParseOptions(int argc, char *argv[])
517 {
518 int i;
519
520 if (argc == 1) {
521 Usage();
522 exit(1);
523 }
524
525 for (i = 1; i < argc; i++) {
526 if (strcmp(argv[i], "--fs") == 0) {
527 FragShaderFile = argv[i+1];
528 i++;
529 }
530 else if (strcmp(argv[i], "--vs") == 0) {
531 VertShaderFile = argv[i+1];
532 i++;
533 }
534 else {
535 /* assume the arg is a config file */
536 ConfigFile = argv[i];
537 break;
538 }
539 }
540 }
541
542
543 int
544 main(int argc, char *argv[])
545 {
546 glutInit(&argc, argv);
547 glutInitWindowPosition( 0, 0);
548 glutInitWindowSize(400, 400);
549 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
550 win = glutCreateWindow(argv[0]);
551 glewInit();
552 glutReshapeFunc(Reshape);
553 glutKeyboardFunc(Key);
554 glutSpecialFunc(SpecialKey);
555 glutDisplayFunc(Redisplay);
556 ParseOptions(argc, argv);
557 Init();
558 Keys();
559 glutMainLoop();
560 return 0;
561 }
562