1/**
2 * Test using a geometry shader to implement wide lines.
3 *
4 * Brian Paul
5 * March 2011
6 */
7
8#include <assert.h>
9#include <string.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <math.h>
13#include <GL/glew.h>
14#include "glut_wrap.h"
15#include "shaderutil.h"
16
17
18static GLint WinWidth = 500, WinHeight = 500;
19static GLint Win = 0;
20static GLuint VertShader, GeomShader, FragShader, Program;
21static GLboolean Anim = GL_TRUE;
22static GLboolean UseGeomShader = GL_TRUE;
23static GLfloat LineWidth = 10.0;
24static GLfloat MaxLineWidth;
25static GLfloat Xrot = 0, Yrot = 0;
26static int uLineWidth = -1, uInverseViewportSize = -1;
27
28static int NumPoints = 50;
29
30static const GLfloat Red[4] = {1, 0, 0, 1};
31static const GLfloat Green[4] = {0, 1, 0, 0};
32
33
34static void
35CheckError(int line)
36{
37   GLenum err = glGetError();
38   if (err) {
39      printf("GL Error %s (0x%x) at line %d\n",
40             gluErrorString(err), (int) err, line);
41   }
42}
43
44
45static void
46Redisplay(void)
47{
48   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
49
50   glPushMatrix();
51   glRotatef(Xrot, 1, 0, 0);
52   glRotatef(Yrot, 0, 0, 1);
53
54   if (UseGeomShader) {
55      glUseProgram(Program);
56      glUniform1f(uLineWidth, LineWidth);
57   }
58   else {
59      glUseProgram(0);
60      glLineWidth(LineWidth);
61   }
62
63   glDrawArrays(GL_LINES, 0, NumPoints / 2);
64
65   glPopMatrix();
66
67   glutSwapBuffers();
68}
69
70
71static void
72Idle(void)
73{
74   int curTime = glutGet(GLUT_ELAPSED_TIME);
75   Xrot = curTime * 0.02;
76   Yrot = curTime * 0.05;
77   glutPostRedisplay();
78}
79
80
81static void
82Reshape(int width, int height)
83{
84   float ar = (float) width / height;
85   glViewport(0, 0, width, height);
86   glMatrixMode(GL_PROJECTION);
87   glLoadIdentity();
88   glFrustum(-ar, ar, -1, 1, 3, 25);
89   glMatrixMode(GL_MODELVIEW);
90   glLoadIdentity();
91   glTranslatef(0, 0, -10);
92
93   {
94      GLfloat viewport[4];
95      glGetFloatv(GL_VIEWPORT, viewport);
96      glUniform2f(uInverseViewportSize, 1.0F / viewport[2], 1.0F / viewport[3]);
97   }
98}
99
100
101static void
102CleanUp(void)
103{
104   glDeleteShader(FragShader);
105   glDeleteShader(VertShader);
106   glDeleteShader(GeomShader);
107   glDeleteProgram(Program);
108   glutDestroyWindow(Win);
109}
110
111
112static void
113Key(unsigned char key, int x, int y)
114{
115  (void) x;
116  (void) y;
117
118   switch(key) {
119   case ' ':
120   case 'a':
121      Anim = !Anim;
122      if (Anim) {
123         glutIdleFunc(Idle);
124      }
125      else
126         glutIdleFunc(NULL);
127      break;
128   case 'g':
129      UseGeomShader = !UseGeomShader;
130      printf("Use geometry shader? %d\n", UseGeomShader);
131      break;
132   case 'w':
133      LineWidth -= 0.5;
134      if (LineWidth < 1.0)
135         LineWidth = 1.0;
136      printf("Line width: %f\n", LineWidth);
137      break;
138   case 'W':
139      LineWidth += 0.5;
140      if (LineWidth > MaxLineWidth)
141         LineWidth = MaxLineWidth;
142      printf("Line width: %f\n", LineWidth);
143      break;
144
145   case 27:
146      CleanUp();
147      exit(0);
148      break;
149   }
150   glutPostRedisplay();
151}
152
153
154static void
155MakePointsVBO(void)
156{
157   struct vert {
158      GLfloat pos[3];
159      GLfloat color[3];
160   };
161   struct vert *v;
162   GLuint vbo;
163   int i;
164
165   glGenBuffers(1, &vbo);
166   glBindBuffer(GL_ARRAY_BUFFER, vbo);
167   glBufferData(GL_ARRAY_BUFFER, NumPoints * sizeof(struct vert),
168                NULL, GL_STATIC_DRAW);
169
170   v = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
171   for (i = 0; i < NumPoints; i++) {
172      v[i].color[0] = (rand() % 1000) / 1000.0;
173      v[i].color[1] = (rand() % 1000) / 1000.0;
174      v[i].color[2] = (rand() % 1000) / 1000.0;
175      v[i].pos[0] = ((rand() % 2000) - 1000.0) / 500.0;
176      v[i].pos[1] = ((rand() % 2000) - 1000.0) / 500.0;
177      v[i].pos[2] = ((rand() % 2000) - 1000.0) / 500.0;
178   }
179   glUnmapBuffer(GL_ARRAY_BUFFER);
180
181   glVertexPointer(3, GL_FLOAT, sizeof(struct vert), (void *) 0);
182   glEnable(GL_VERTEX_ARRAY);
183   glColorPointer(3, GL_FLOAT, sizeof(struct vert), (void *) sizeof(float[3]));
184   glEnable(GL_COLOR_ARRAY);
185}
186
187
188static void
189Init(void)
190{
191   static const char *fragShaderText =
192      "void main() \n"
193      "{ \n"
194      "   gl_FragColor = gl_Color; \n"
195      "} \n";
196   static const char *vertShaderText =
197      "void main() \n"
198      "{ \n"
199      "   gl_FrontColor = gl_Color; \n"
200      "   gl_Position = ftransform(); \n"
201      "} \n";
202   static const char *geomShaderText =
203      "#version 120 \n"
204      "#extension GL_ARB_geometry_shader4: enable \n"
205      "uniform vec2 InverseViewportSize; \n"
206      "uniform float LineWidth; \n"
207      "void main() \n"
208      "{ \n"
209      "   vec4 pos0 = gl_PositionIn[0]; \n"
210      "   vec4 pos1 = gl_PositionIn[1]; \n"
211      "   vec4 dir = abs(pos1 - pos0); \n"
212      "   vec2 d0 = vec2(LineWidth * pos0.w) * InverseViewportSize; \n"
213      "   vec2 d1 = vec2(LineWidth * pos1.w) * InverseViewportSize; \n"
214      "   // this conditional could be avoided \n"
215      "   if (dir.x > dir.y) { \n"
216      "      // X-major line \n"
217      "      d0.x = 0.0; \n"
218      "      d1.x = 0.0; \n"
219      "   } \n"
220      "   else { \n"
221      "      // Y-major line \n"
222      "      d0.y = 0.0; \n"
223      "      d1.y = 0.0; \n"
224      "   } \n"
225      "   gl_FrontColor = gl_FrontColorIn[0]; \n"
226      "   gl_TexCoord[0] = vec4(0, 0, 0, 1); \n"
227      "   gl_Position = pos0 + vec4( d0.x, -d0.y, 0, 0); \n"
228      "   EmitVertex(); \n"
229      "   gl_FrontColor = gl_FrontColorIn[1]; \n"
230      "   gl_TexCoord[0] = vec4(1, 0, 0, 1); \n"
231      "   gl_Position = pos1 + vec4( d1.x, -d1.y, 0, 0); \n"
232      "   EmitVertex(); \n"
233      "   gl_FrontColor = gl_FrontColorIn[0]; \n"
234      "   gl_TexCoord[0] = vec4(0, 1, 0, 1); \n"
235      "   gl_Position = pos0 + vec4(-d0.x,  d0.y, 0, 0); \n"
236      "   EmitVertex(); \n"
237      "   gl_FrontColor = gl_FrontColorIn[1]; \n"
238      "   gl_TexCoord[0] = vec4(1, 1, 0, 1); \n"
239      "   gl_Position = pos1 + vec4(-d1.x,  d1.y, 0, 0); \n"
240      "   EmitVertex(); \n"
241      "} \n";
242
243   if (!ShadersSupported())
244      exit(1);
245
246   if (!glutExtensionSupported("GL_ARB_geometry_shader4")) {
247      fprintf(stderr, "Sorry, GL_ARB_geometry_shader4 is not supported.\n");
248      exit(1);
249   }
250
251   VertShader = CompileShaderText(GL_VERTEX_SHADER, vertShaderText);
252   FragShader = CompileShaderText(GL_FRAGMENT_SHADER, fragShaderText);
253   GeomShader = CompileShaderText(GL_GEOMETRY_SHADER_ARB, geomShaderText);
254   assert(GeomShader);
255
256   Program = LinkShaders3(VertShader, GeomShader, FragShader);
257   assert(Program);
258   CheckError(__LINE__);
259
260   /*
261    * The geometry shader will convert incoming lines to quads (4-vertex
262    * triangle strips).
263    */
264   glProgramParameteriARB(Program, GL_GEOMETRY_INPUT_TYPE_ARB,
265                          GL_LINES);
266   glProgramParameteriARB(Program, GL_GEOMETRY_OUTPUT_TYPE_ARB,
267                          GL_TRIANGLE_STRIP);
268   glProgramParameteriARB(Program,GL_GEOMETRY_VERTICES_OUT_ARB, 4);
269   CheckError(__LINE__);
270
271   glLinkProgramARB(Program);
272
273   /* check link */
274   {
275      GLint stat;
276      GetProgramiv(Program, GL_LINK_STATUS, &stat);
277      if (!stat) {
278         GLchar log[1000];
279         GLsizei len;
280         GetProgramInfoLog(Program, 1000, &len, log);
281         fprintf(stderr, "Shader link error:\n%s\n", log);
282      }
283   }
284
285   CheckError(__LINE__);
286
287   glUseProgram(Program);
288   CheckError(__LINE__);
289
290   uInverseViewportSize = glGetUniformLocation(Program, "InverseViewportSize");
291   uLineWidth = glGetUniformLocation(Program, "LineWidth");
292
293   glClearColor(0.3f, 0.3f, 0.3f, 0.0f);
294
295   printf("GL_RENDERER = %s\n",(const char *) glGetString(GL_RENDERER));
296
297   assert(glIsProgram(Program));
298   assert(glIsShader(FragShader));
299   assert(glIsShader(VertShader));
300   assert(glIsShader(GeomShader));
301
302   glEnable(GL_DEPTH_TEST);
303
304   {
305      GLfloat r[2];
306      glGetFloatv(GL_LINE_WIDTH_RANGE, r);
307      MaxLineWidth = r[1];
308   }
309
310   MakePointsVBO();
311}
312
313
314int
315main(int argc, char *argv[])
316{
317   glutInit(&argc, argv);
318
319   if (argc > 1) {
320      int n = atoi(argv[1]);
321      if (n > 0) {
322         NumPoints = n;
323      }
324      else {
325         printf("Invalid number of points\n");
326         return 1;
327      }
328   }
329
330   glutInitWindowSize(WinWidth, WinHeight);
331   glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
332   Win = glutCreateWindow(argv[0]);
333   glewInit();
334   glutReshapeFunc(Reshape);
335   glutKeyboardFunc(Key);
336   glutDisplayFunc(Redisplay);
337   if (Anim)
338      glutIdleFunc(Idle);
339
340   Init();
341   glutMainLoop();
342   return 0;
343}
344