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