blob: 54b12cc50ba0aade91bb449dc761dd1b6e7c0c08 [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) {
260 Sphere(2.0, 20, 10);
261 }
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 },
429 { NULL, 0 }
430 };
431 GLuint i;
432
433 for (i = 0; types[i].name; i++) {
434 if (strcmp(types[i].name, n) == 0)
435 return types[i].type;
436 }
437 abort();
438 return GL_NONE;
439}
440
441
442
Brian Paulae99e4c2009-08-13 12:52:13 -0600443/**
444 * Read a config file.
445 */
446static void
447ReadConfigFile(const char *filename, struct config_file *conf)
448{
449 char line[1000];
450 FILE *f;
451
452 f = fopen(filename, "r");
453 if (!f) {
454 fprintf(stderr, "Unable to open config file %s\n", filename);
455 exit(1);
456 }
457
458 conf->num_uniforms = 0;
459
460 /* ugly but functional parser */
461 while (!feof(f)) {
462 fgets(line, sizeof(line), f);
Brian Paul62d11322009-08-13 15:53:49 -0600463 if (!feof(f) && line[0]) {
Brian Paulae99e4c2009-08-13 12:52:13 -0600464 if (strncmp(line, "vs ", 3) == 0) {
465 VertShaderFile = strdup(line + 3);
466 VertShaderFile[strlen(VertShaderFile) - 1] = 0;
467 }
468 else if (strncmp(line, "fs ", 3) == 0) {
469 FragShaderFile = strdup(line + 3);
470 FragShaderFile[strlen(FragShaderFile) - 1] = 0;
471 }
Brian Paul62d11322009-08-13 15:53:49 -0600472 else if (strncmp(line, "texture ", 8) == 0) {
473 char texFileName[100];
474 int unit, k;
475 k = sscanf(line + 8, "%d %s", &unit, texFileName);
476 assert(k == 2);
477 LoadTexture(unit, texFileName);
478 }
Brian Paulae99e4c2009-08-13 12:52:13 -0600479 else if (strncmp(line, "uniform ", 8) == 0) {
Brian Paul62d11322009-08-13 15:53:49 -0600480 char name[1000], typeName[100];
Brian Paulae99e4c2009-08-13 12:52:13 -0600481 int k;
Brian Paul62d11322009-08-13 15:53:49 -0600482 float v1 = 0.0F, v2 = 0.0F, v3 = 0.0F, v4 = 0.0F;
Brian Paulae99e4c2009-08-13 12:52:13 -0600483 GLenum type;
484
Brian Paul62d11322009-08-13 15:53:49 -0600485 k = sscanf(line + 8, "%s %s %f %f %f %f", name, typeName,
486 &v1, &v2, &v3, &v4);
Brian Paulae99e4c2009-08-13 12:52:13 -0600487
Brian Paul62d11322009-08-13 15:53:49 -0600488 type = TypeFromName(typeName);
Brian Paulae99e4c2009-08-13 12:52:13 -0600489
490 strcpy(conf->uniforms[conf->num_uniforms].name, name);
491 conf->uniforms[conf->num_uniforms].value[0] = v1;
492 conf->uniforms[conf->num_uniforms].value[1] = v2;
493 conf->uniforms[conf->num_uniforms].value[2] = v3;
494 conf->uniforms[conf->num_uniforms].value[3] = v4;
495 conf->uniforms[conf->num_uniforms].type = type;
496 conf->num_uniforms++;
497 }
498 else {
499 if (strlen(line) > 1) {
500 fprintf(stderr, "syntax error in: %s\n", line);
501 break;
502 }
503 }
504 }
505 }
506
507 fclose(f);
508}
509
510
511static void
512Init(void)
513{
514 struct config_file config;
515 memset(&config, 0, sizeof(config));
516
517 if (ConfigFile)
518 ReadConfigFile(ConfigFile, &config);
519
520 if (!VertShaderFile) {
521 fprintf(stderr, "Error: no vertex shader\n");
522 exit(1);
523 }
524
525 if (!FragShaderFile) {
526 fprintf(stderr, "Error: no fragment shader\n");
527 exit(1);
528 }
529
530 if (!ShadersSupported())
531 exit(1);
532
533 vertShader = CompileShaderFile(GL_VERTEX_SHADER, VertShaderFile);
534 fragShader = CompileShaderFile(GL_FRAGMENT_SHADER, FragShaderFile);
535 Program = LinkShaders(vertShader, fragShader);
536
537 glUseProgram(Program);
538
539 NumUniforms = GetUniforms(Program, Uniforms);
540 if (config.num_uniforms) {
541 InitUniforms(&config, Uniforms);
542 }
543 else {
544 RandomUniformValues();
545 }
546 SetUniformValues(Program, Uniforms);
547 PrintUniforms(Uniforms);
548
549 NumAttribs = GetAttribs(Program, Attribs);
550 PrintAttribs(Attribs);
551
552 //assert(glGetError() == 0);
553
554 glClearColor(0.4f, 0.4f, 0.8f, 0.0f);
555
556 glEnable(GL_DEPTH_TEST);
557
558 glColor3f(1, 0, 0);
559}
560
561
562static void
563Keys(void)
564{
565 printf("Keyboard:\n");
566 printf(" a Animation toggle\n");
567 printf(" r Randomize uniform values\n");
568 printf(" o Change object\n");
569 printf(" arrows Rotate object\n");
570 printf(" ESC Exit\n");
571}
572
573
574static void
575Usage(void)
576{
577 printf("Usage:\n");
578 printf(" shtest config.shtest\n");
579 printf(" Run w/ given config file.\n");
580 printf(" shtest --vs vertShader --fs fragShader\n");
581 printf(" Load/compile given shaders.\n");
582}
583
584
585static void
586ParseOptions(int argc, char *argv[])
587{
588 int i;
589
590 if (argc == 1) {
591 Usage();
592 exit(1);
593 }
594
595 for (i = 1; i < argc; i++) {
596 if (strcmp(argv[i], "--fs") == 0) {
597 FragShaderFile = argv[i+1];
598 i++;
599 }
600 else if (strcmp(argv[i], "--vs") == 0) {
601 VertShaderFile = argv[i+1];
602 i++;
603 }
604 else {
605 /* assume the arg is a config file */
606 ConfigFile = argv[i];
607 break;
608 }
609 }
610}
611
612
613int
614main(int argc, char *argv[])
615{
616 glutInit(&argc, argv);
Brian Paulae99e4c2009-08-13 12:52:13 -0600617 glutInitWindowSize(400, 400);
618 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
619 win = glutCreateWindow(argv[0]);
620 glewInit();
621 glutReshapeFunc(Reshape);
622 glutKeyboardFunc(Key);
623 glutSpecialFunc(SpecialKey);
624 glutDisplayFunc(Redisplay);
625 ParseOptions(argc, argv);
626 Init();
627 Keys();
628 glutMainLoop();
629 return 0;
630}
631