132001f49Smrg/**
232001f49Smrg * Test using a geometry shader to implement wide lines.
332001f49Smrg *
432001f49Smrg * Brian Paul
532001f49Smrg * March 2011
632001f49Smrg */
732001f49Smrg
832001f49Smrg#include <assert.h>
932001f49Smrg#include <string.h>
1032001f49Smrg#include <stdio.h>
1132001f49Smrg#include <stdlib.h>
1232001f49Smrg#include <math.h>
1332001f49Smrg#include <GL/glew.h>
1432001f49Smrg#include "glut_wrap.h"
1532001f49Smrg#include "shaderutil.h"
1632001f49Smrg
1732001f49Smrg
1832001f49Smrgstatic GLint WinWidth = 500, WinHeight = 500;
1932001f49Smrgstatic GLint Win = 0;
2032001f49Smrgstatic GLuint VertShader, GeomShader, FragShader, Program;
2132001f49Smrgstatic GLboolean Anim = GL_TRUE;
2232001f49Smrgstatic GLboolean UseGeomShader = GL_TRUE;
2332001f49Smrgstatic GLfloat LineWidth = 10.0;
2432001f49Smrgstatic GLfloat MaxLineWidth;
2532001f49Smrgstatic GLfloat Xrot = 0, Yrot = 0;
2632001f49Smrgstatic int uLineWidth = -1, uInverseViewportSize = -1;
2732001f49Smrg
287ec3b29aSmrgstatic int NumPoints = 50;
2932001f49Smrg
3032001f49Smrgstatic const GLfloat Red[4] = {1, 0, 0, 1};
3132001f49Smrgstatic const GLfloat Green[4] = {0, 1, 0, 0};
3232001f49Smrg
3332001f49Smrg
3432001f49Smrgstatic void
3532001f49SmrgCheckError(int line)
3632001f49Smrg{
3732001f49Smrg   GLenum err = glGetError();
3832001f49Smrg   if (err) {
3932001f49Smrg      printf("GL Error %s (0x%x) at line %d\n",
4032001f49Smrg             gluErrorString(err), (int) err, line);
4132001f49Smrg   }
4232001f49Smrg}
4332001f49Smrg
4432001f49Smrg
4532001f49Smrgstatic void
4632001f49SmrgRedisplay(void)
4732001f49Smrg{
4832001f49Smrg   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
4932001f49Smrg
5032001f49Smrg   glPushMatrix();
5132001f49Smrg   glRotatef(Xrot, 1, 0, 0);
5232001f49Smrg   glRotatef(Yrot, 0, 0, 1);
5332001f49Smrg
5432001f49Smrg   if (UseGeomShader) {
5532001f49Smrg      glUseProgram(Program);
5632001f49Smrg      glUniform1f(uLineWidth, LineWidth);
5732001f49Smrg   }
5832001f49Smrg   else {
5932001f49Smrg      glUseProgram(0);
6032001f49Smrg      glLineWidth(LineWidth);
6132001f49Smrg   }
6232001f49Smrg
637ec3b29aSmrg   glDrawArrays(GL_LINES, 0, NumPoints / 2);
6432001f49Smrg
6532001f49Smrg   glPopMatrix();
6632001f49Smrg
6732001f49Smrg   glutSwapBuffers();
6832001f49Smrg}
6932001f49Smrg
7032001f49Smrg
7132001f49Smrgstatic void
7232001f49SmrgIdle(void)
7332001f49Smrg{
7432001f49Smrg   int curTime = glutGet(GLUT_ELAPSED_TIME);
7532001f49Smrg   Xrot = curTime * 0.02;
7632001f49Smrg   Yrot = curTime * 0.05;
7732001f49Smrg   glutPostRedisplay();
7832001f49Smrg}
7932001f49Smrg
8032001f49Smrg
8132001f49Smrgstatic void
8232001f49SmrgReshape(int width, int height)
8332001f49Smrg{
8432001f49Smrg   float ar = (float) width / height;
8532001f49Smrg   glViewport(0, 0, width, height);
8632001f49Smrg   glMatrixMode(GL_PROJECTION);
8732001f49Smrg   glLoadIdentity();
8832001f49Smrg   glFrustum(-ar, ar, -1, 1, 3, 25);
8932001f49Smrg   glMatrixMode(GL_MODELVIEW);
9032001f49Smrg   glLoadIdentity();
9132001f49Smrg   glTranslatef(0, 0, -10);
9232001f49Smrg
9332001f49Smrg   {
9432001f49Smrg      GLfloat viewport[4];
9532001f49Smrg      glGetFloatv(GL_VIEWPORT, viewport);
9632001f49Smrg      glUniform2f(uInverseViewportSize, 1.0F / viewport[2], 1.0F / viewport[3]);
9732001f49Smrg   }
9832001f49Smrg}
9932001f49Smrg
10032001f49Smrg
10132001f49Smrgstatic void
10232001f49SmrgCleanUp(void)
10332001f49Smrg{
10432001f49Smrg   glDeleteShader(FragShader);
10532001f49Smrg   glDeleteShader(VertShader);
10632001f49Smrg   glDeleteShader(GeomShader);
10732001f49Smrg   glDeleteProgram(Program);
10832001f49Smrg   glutDestroyWindow(Win);
10932001f49Smrg}
11032001f49Smrg
11132001f49Smrg
11232001f49Smrgstatic void
11332001f49SmrgKey(unsigned char key, int x, int y)
11432001f49Smrg{
11532001f49Smrg  (void) x;
11632001f49Smrg  (void) y;
11732001f49Smrg
11832001f49Smrg   switch(key) {
11932001f49Smrg   case ' ':
12032001f49Smrg   case 'a':
12132001f49Smrg      Anim = !Anim;
12232001f49Smrg      if (Anim) {
12332001f49Smrg         glutIdleFunc(Idle);
12432001f49Smrg      }
12532001f49Smrg      else
12632001f49Smrg         glutIdleFunc(NULL);
12732001f49Smrg      break;
12832001f49Smrg   case 'g':
12932001f49Smrg      UseGeomShader = !UseGeomShader;
13032001f49Smrg      printf("Use geometry shader? %d\n", UseGeomShader);
13132001f49Smrg      break;
13232001f49Smrg   case 'w':
13332001f49Smrg      LineWidth -= 0.5;
13432001f49Smrg      if (LineWidth < 1.0)
13532001f49Smrg         LineWidth = 1.0;
13632001f49Smrg      printf("Line width: %f\n", LineWidth);
13732001f49Smrg      break;
13832001f49Smrg   case 'W':
13932001f49Smrg      LineWidth += 0.5;
14032001f49Smrg      if (LineWidth > MaxLineWidth)
14132001f49Smrg         LineWidth = MaxLineWidth;
14232001f49Smrg      printf("Line width: %f\n", LineWidth);
14332001f49Smrg      break;
14432001f49Smrg
14532001f49Smrg   case 27:
14632001f49Smrg      CleanUp();
14732001f49Smrg      exit(0);
14832001f49Smrg      break;
14932001f49Smrg   }
15032001f49Smrg   glutPostRedisplay();
15132001f49Smrg}
15232001f49Smrg
15332001f49Smrg
15432001f49Smrgstatic void
1557ec3b29aSmrgMakePointsVBO(void)
15632001f49Smrg{
1577ec3b29aSmrg   struct vert {
1587ec3b29aSmrg      GLfloat pos[3];
1597ec3b29aSmrg      GLfloat color[3];
1607ec3b29aSmrg   };
1617ec3b29aSmrg   struct vert *v;
1627ec3b29aSmrg   GLuint vbo;
16332001f49Smrg   int i;
1647ec3b29aSmrg
1657ec3b29aSmrg   glGenBuffers(1, &vbo);
1667ec3b29aSmrg   glBindBuffer(GL_ARRAY_BUFFER, vbo);
1677ec3b29aSmrg   glBufferData(GL_ARRAY_BUFFER, NumPoints * sizeof(struct vert),
1687ec3b29aSmrg                NULL, GL_STATIC_DRAW);
1697ec3b29aSmrg
1707ec3b29aSmrg   v = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
17132001f49Smrg   for (i = 0; i < NumPoints; i++) {
1727ec3b29aSmrg      v[i].color[0] = (rand() % 1000) / 1000.0;
1737ec3b29aSmrg      v[i].color[1] = (rand() % 1000) / 1000.0;
1747ec3b29aSmrg      v[i].color[2] = (rand() % 1000) / 1000.0;
1757ec3b29aSmrg      v[i].pos[0] = ((rand() % 2000) - 1000.0) / 500.0;
1767ec3b29aSmrg      v[i].pos[1] = ((rand() % 2000) - 1000.0) / 500.0;
1777ec3b29aSmrg      v[i].pos[2] = ((rand() % 2000) - 1000.0) / 500.0;
17832001f49Smrg   }
1797ec3b29aSmrg   glUnmapBuffer(GL_ARRAY_BUFFER);
1807ec3b29aSmrg
1817ec3b29aSmrg   glVertexPointer(3, GL_FLOAT, sizeof(struct vert), (void *) 0);
1827ec3b29aSmrg   glEnable(GL_VERTEX_ARRAY);
1837ec3b29aSmrg   glColorPointer(3, GL_FLOAT, sizeof(struct vert), (void *) sizeof(float[3]));
1847ec3b29aSmrg   glEnable(GL_COLOR_ARRAY);
18532001f49Smrg}
18632001f49Smrg
1877ec3b29aSmrg
18832001f49Smrgstatic void
18932001f49SmrgInit(void)
19032001f49Smrg{
19132001f49Smrg   static const char *fragShaderText =
19232001f49Smrg      "void main() \n"
19332001f49Smrg      "{ \n"
19432001f49Smrg      "   gl_FragColor = gl_Color; \n"
19532001f49Smrg      "} \n";
19632001f49Smrg   static const char *vertShaderText =
19732001f49Smrg      "void main() \n"
19832001f49Smrg      "{ \n"
19932001f49Smrg      "   gl_FrontColor = gl_Color; \n"
20032001f49Smrg      "   gl_Position = ftransform(); \n"
20132001f49Smrg      "} \n";
20232001f49Smrg   static const char *geomShaderText =
20332001f49Smrg      "#version 120 \n"
20432001f49Smrg      "#extension GL_ARB_geometry_shader4: enable \n"
20532001f49Smrg      "uniform vec2 InverseViewportSize; \n"
20632001f49Smrg      "uniform float LineWidth; \n"
20732001f49Smrg      "void main() \n"
20832001f49Smrg      "{ \n"
20932001f49Smrg      "   vec4 pos0 = gl_PositionIn[0]; \n"
21032001f49Smrg      "   vec4 pos1 = gl_PositionIn[1]; \n"
21132001f49Smrg      "   vec4 dir = abs(pos1 - pos0); \n"
21232001f49Smrg      "   vec2 d0 = vec2(LineWidth * pos0.w) * InverseViewportSize; \n"
21332001f49Smrg      "   vec2 d1 = vec2(LineWidth * pos1.w) * InverseViewportSize; \n"
21432001f49Smrg      "   // this conditional could be avoided \n"
21532001f49Smrg      "   if (dir.x > dir.y) { \n"
21632001f49Smrg      "      // X-major line \n"
21732001f49Smrg      "      d0.x = 0.0; \n"
21832001f49Smrg      "      d1.x = 0.0; \n"
21932001f49Smrg      "   } \n"
22032001f49Smrg      "   else { \n"
22132001f49Smrg      "      // Y-major line \n"
22232001f49Smrg      "      d0.y = 0.0; \n"
22332001f49Smrg      "      d1.y = 0.0; \n"
22432001f49Smrg      "   } \n"
22532001f49Smrg      "   gl_FrontColor = gl_FrontColorIn[0]; \n"
22632001f49Smrg      "   gl_TexCoord[0] = vec4(0, 0, 0, 1); \n"
22732001f49Smrg      "   gl_Position = pos0 + vec4( d0.x, -d0.y, 0, 0); \n"
22832001f49Smrg      "   EmitVertex(); \n"
22932001f49Smrg      "   gl_FrontColor = gl_FrontColorIn[1]; \n"
23032001f49Smrg      "   gl_TexCoord[0] = vec4(1, 0, 0, 1); \n"
23132001f49Smrg      "   gl_Position = pos1 + vec4( d1.x, -d1.y, 0, 0); \n"
23232001f49Smrg      "   EmitVertex(); \n"
23332001f49Smrg      "   gl_FrontColor = gl_FrontColorIn[0]; \n"
23432001f49Smrg      "   gl_TexCoord[0] = vec4(0, 1, 0, 1); \n"
23532001f49Smrg      "   gl_Position = pos0 + vec4(-d0.x,  d0.y, 0, 0); \n"
23632001f49Smrg      "   EmitVertex(); \n"
23732001f49Smrg      "   gl_FrontColor = gl_FrontColorIn[1]; \n"
23832001f49Smrg      "   gl_TexCoord[0] = vec4(1, 1, 0, 1); \n"
23932001f49Smrg      "   gl_Position = pos1 + vec4(-d1.x,  d1.y, 0, 0); \n"
24032001f49Smrg      "   EmitVertex(); \n"
24132001f49Smrg      "} \n";
24232001f49Smrg
24332001f49Smrg   if (!ShadersSupported())
24432001f49Smrg      exit(1);
24532001f49Smrg
24632001f49Smrg   if (!glutExtensionSupported("GL_ARB_geometry_shader4")) {
24732001f49Smrg      fprintf(stderr, "Sorry, GL_ARB_geometry_shader4 is not supported.\n");
24832001f49Smrg      exit(1);
24932001f49Smrg   }
25032001f49Smrg
25132001f49Smrg   VertShader = CompileShaderText(GL_VERTEX_SHADER, vertShaderText);
25232001f49Smrg   FragShader = CompileShaderText(GL_FRAGMENT_SHADER, fragShaderText);
25332001f49Smrg   GeomShader = CompileShaderText(GL_GEOMETRY_SHADER_ARB, geomShaderText);
25432001f49Smrg   assert(GeomShader);
25532001f49Smrg
25632001f49Smrg   Program = LinkShaders3(VertShader, GeomShader, FragShader);
25732001f49Smrg   assert(Program);
25832001f49Smrg   CheckError(__LINE__);
25932001f49Smrg
26032001f49Smrg   /*
26132001f49Smrg    * The geometry shader will convert incoming lines to quads (4-vertex
26232001f49Smrg    * triangle strips).
26332001f49Smrg    */
26432001f49Smrg   glProgramParameteriARB(Program, GL_GEOMETRY_INPUT_TYPE_ARB,
26532001f49Smrg                          GL_LINES);
26632001f49Smrg   glProgramParameteriARB(Program, GL_GEOMETRY_OUTPUT_TYPE_ARB,
26732001f49Smrg                          GL_TRIANGLE_STRIP);
26832001f49Smrg   glProgramParameteriARB(Program,GL_GEOMETRY_VERTICES_OUT_ARB, 4);
26932001f49Smrg   CheckError(__LINE__);
27032001f49Smrg
27132001f49Smrg   glLinkProgramARB(Program);
27232001f49Smrg
27332001f49Smrg   /* check link */
27432001f49Smrg   {
27532001f49Smrg      GLint stat;
27632001f49Smrg      GetProgramiv(Program, GL_LINK_STATUS, &stat);
27732001f49Smrg      if (!stat) {
27832001f49Smrg         GLchar log[1000];
27932001f49Smrg         GLsizei len;
28032001f49Smrg         GetProgramInfoLog(Program, 1000, &len, log);
28132001f49Smrg         fprintf(stderr, "Shader link error:\n%s\n", log);
28232001f49Smrg      }
28332001f49Smrg   }
28432001f49Smrg
28532001f49Smrg   CheckError(__LINE__);
28632001f49Smrg
28732001f49Smrg   glUseProgram(Program);
28832001f49Smrg   CheckError(__LINE__);
28932001f49Smrg
29032001f49Smrg   uInverseViewportSize = glGetUniformLocation(Program, "InverseViewportSize");
29132001f49Smrg   uLineWidth = glGetUniformLocation(Program, "LineWidth");
29232001f49Smrg
29332001f49Smrg   glClearColor(0.3f, 0.3f, 0.3f, 0.0f);
29432001f49Smrg
29532001f49Smrg   printf("GL_RENDERER = %s\n",(const char *) glGetString(GL_RENDERER));
29632001f49Smrg
29732001f49Smrg   assert(glIsProgram(Program));
29832001f49Smrg   assert(glIsShader(FragShader));
29932001f49Smrg   assert(glIsShader(VertShader));
30032001f49Smrg   assert(glIsShader(GeomShader));
30132001f49Smrg
30232001f49Smrg   glEnable(GL_DEPTH_TEST);
30332001f49Smrg
30432001f49Smrg   {
30532001f49Smrg      GLfloat r[2];
30632001f49Smrg      glGetFloatv(GL_LINE_WIDTH_RANGE, r);
30732001f49Smrg      MaxLineWidth = r[1];
30832001f49Smrg   }
30932001f49Smrg
3107ec3b29aSmrg   MakePointsVBO();
31132001f49Smrg}
31232001f49Smrg
31332001f49Smrg
31432001f49Smrgint
31532001f49Smrgmain(int argc, char *argv[])
31632001f49Smrg{
31732001f49Smrg   glutInit(&argc, argv);
3187ec3b29aSmrg
3197ec3b29aSmrg   if (argc > 1) {
3207ec3b29aSmrg      int n = atoi(argv[1]);
3217ec3b29aSmrg      if (n > 0) {
3227ec3b29aSmrg         NumPoints = n;
3237ec3b29aSmrg      }
3247ec3b29aSmrg      else {
3257ec3b29aSmrg         printf("Invalid number of points\n");
3267ec3b29aSmrg         return 1;
3277ec3b29aSmrg      }
3287ec3b29aSmrg   }
3297ec3b29aSmrg
33032001f49Smrg   glutInitWindowSize(WinWidth, WinHeight);
33132001f49Smrg   glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
33232001f49Smrg   Win = glutCreateWindow(argv[0]);
33332001f49Smrg   glewInit();
33432001f49Smrg   glutReshapeFunc(Reshape);
33532001f49Smrg   glutKeyboardFunc(Key);
33632001f49Smrg   glutDisplayFunc(Redisplay);
33732001f49Smrg   if (Anim)
33832001f49Smrg      glutIdleFunc(Idle);
33932001f49Smrg
34032001f49Smrg   Init();
34132001f49Smrg   glutMainLoop();
34232001f49Smrg   return 0;
34332001f49Smrg}
344