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