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