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