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