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