1/**
2 * Test using a geometry and fragment shaders to implement stippled lines.
3 *
4 * Brian Paul
5 * April 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
17static GLint WinWidth = 500, WinHeight = 500;
18static GLint Win = 0;
19static GLuint VertShader, GeomShader, FragShader, Program;
20static GLboolean Anim = GL_TRUE;
21static GLboolean UseGeomShader = GL_TRUE;
22static GLfloat Xrot = 0, Yrot = 0;
23static int uViewportSize = -1, uStippleFactor = -1, uStipplePattern = -1;
24static int NumPoints = 50;
25
26static const GLushort StipplePattern = 0x10ff;
27static GLuint StippleFactor = 2;
28
29
30static void
31CheckError(int line)
32{
33   GLenum err = glGetError();
34   if (err) {
35      printf("GL Error %s (0x%x) at line %d\n",
36             gluErrorString(err), (int) err, line);
37   }
38}
39
40
41/**
42 * Set stipple factor and pattern for geometry shader.
43 *
44 * We convert the 16-bit stipple pattern into an array of 16 float values
45 * then pass the array as a uniform variable.
46 *
47 * Note: With GLSL 1.30 or later the stipple pattern could be implemented
48 * as an ordinary integer since GLSL 1.30 has true integer types and bit
49 * shifts and bit masks.
50 *
51 */
52static void
53SetStippleUniform(GLint factor, GLushort pattern)
54{
55   GLfloat p[16];
56   int i;
57   for (i = 0; i < 16; i++) {
58      p[i] = (pattern & (1 << i)) ? 1.0f : 0.0f;
59   }
60   glUniform1fv(uStipplePattern, 16, p);
61   glUniform1f(uStippleFactor, factor);
62}
63
64
65static void
66Redisplay(void)
67{
68   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
69
70   glPushMatrix();
71   glRotatef(Xrot, 1, 0, 0);
72   glRotatef(Yrot, 0, 0, 1);
73
74   if (UseGeomShader) {
75      glUseProgram(Program);
76      glDisable(GL_LINE_STIPPLE);
77   }
78   else {
79      glUseProgram(0);
80      glEnable(GL_LINE_STIPPLE);
81   }
82
83   glDrawArrays(GL_LINES, 0, NumPoints / 2);
84
85   glPopMatrix();
86
87   glutSwapBuffers();
88}
89
90
91static void
92Idle(void)
93{
94   int curTime = glutGet(GLUT_ELAPSED_TIME);
95   Xrot = curTime * 0.02;
96   Yrot = curTime * 0.05;
97   glutPostRedisplay();
98}
99
100
101static void
102Reshape(int width, int height)
103{
104   float ar = (float) width / height;
105   glViewport(0, 0, width, height);
106   glMatrixMode(GL_PROJECTION);
107   glLoadIdentity();
108#if 1
109   glFrustum(-ar, ar, -1, 1, 3, 25);
110#else
111   glOrtho(-3.0*ar, 3.0*ar, -3.0, 3.0, 3, 25);
112#endif
113   glMatrixMode(GL_MODELVIEW);
114   glLoadIdentity();
115   glTranslatef(0, 0, -10);
116
117   {
118      GLfloat viewport[4];
119      glGetFloatv(GL_VIEWPORT, viewport);
120      glUniform2f(uViewportSize, viewport[2], viewport[3]);
121   }
122}
123
124
125static void
126CleanUp(void)
127{
128   glDeleteShader(FragShader);
129   glDeleteShader(VertShader);
130   glDeleteShader(GeomShader);
131   glDeleteProgram(Program);
132   glutDestroyWindow(Win);
133}
134
135
136static void
137Key(unsigned char key, int x, int y)
138{
139  (void) x;
140  (void) y;
141
142   switch(key) {
143   case ' ':
144   case 'a':
145      Anim = !Anim;
146      if (Anim) {
147         glutIdleFunc(Idle);
148      }
149      else
150         glutIdleFunc(NULL);
151      break;
152   case 'g':
153      UseGeomShader = !UseGeomShader;
154      printf("Use geometry shader? %d\n", UseGeomShader);
155      break;
156   case 'x':
157      Xrot ++;
158      break;
159   case 27:
160      CleanUp();
161      exit(0);
162      break;
163   }
164   glutPostRedisplay();
165}
166
167
168static void
169MakePointsVBO(void)
170{
171   struct vert {
172      GLfloat pos[3];
173      GLfloat color[3];
174   };
175   struct vert *v;
176   GLuint vbo;
177   int i;
178
179   glGenBuffers(1, &vbo);
180   glBindBuffer(GL_ARRAY_BUFFER, vbo);
181   glBufferData(GL_ARRAY_BUFFER, NumPoints * sizeof(struct vert),
182                NULL, GL_STATIC_DRAW);
183
184   v = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
185   for (i = 0; i < NumPoints; i++) {
186      v[i].color[0] = (rand() % 1000) / 1000.0;
187      v[i].color[1] = (rand() % 1000) / 1000.0;
188      v[i].color[2] = (rand() % 1000) / 1000.0;
189      v[i].pos[0] = ((rand() % 2000) - 1000.0) / 500.0;
190      v[i].pos[1] = ((rand() % 2000) - 1000.0) / 500.0;
191      v[i].pos[2] = ((rand() % 2000) - 1000.0) / 500.0;
192   }
193   glUnmapBuffer(GL_ARRAY_BUFFER);
194
195   glVertexPointer(3, GL_FLOAT, sizeof(struct vert), (void *) 0);
196   glEnable(GL_VERTEX_ARRAY);
197   glColorPointer(3, GL_FLOAT, sizeof(struct vert), (void *) sizeof(float[3]));
198   glEnable(GL_COLOR_ARRAY);
199}
200
201
202static void
203Init(void)
204{
205   static const char *fragShaderText =
206      "uniform float StipplePattern[16]; \n"
207      "varying float stippleCoord; \n"
208      "void main() \n"
209      "{ \n"
210      "   // check the stipple pattern and discard if value is zero \n"
211      "   // TODO: we should really undo the perspective interpolation here \n"
212      "   // so that it's linear. \n"
213      "   float stip = StipplePattern[int(fract(stippleCoord) * 16.0)]; \n"
214      "   if (stip == 0.0) \n"
215      "      discard; \n"
216      "   gl_FragColor = gl_Color; \n"
217      "} \n";
218   static const char *vertShaderText =
219      "void main() \n"
220      "{ \n"
221      "   gl_FrontColor = gl_Color; \n"
222      "   gl_Position = ftransform(); \n"
223      "} \n";
224   static const char *geomShaderText =
225      "#version 120 \n"
226      "#extension GL_ARB_geometry_shader4: enable \n"
227      "uniform vec2 ViewportSize; \n"
228      "uniform float StippleFactor; \n"
229      "varying float stippleCoord; \n"
230      "void main() \n"
231      "{ \n"
232      "   vec4 pos0 = gl_PositionIn[0]; \n"
233      "   vec4 pos1 = gl_PositionIn[1]; \n"
234      "   // Convert eye coords to window coords \n"
235      "   // Note: we're off by a factor of two here, make up for that below \n"
236      "   vec2 p0 = pos0.xy / pos0.w * ViewportSize; \n"
237      "   vec2 p1 = pos1.xy / pos1.w * ViewportSize; \n"
238      "   float len = length(p0.xy - p1.xy); \n"
239      "   // Emit first vertex \n"
240      "   gl_FrontColor = gl_FrontColorIn[0]; \n"
241      "   gl_Position = pos0; \n"
242      "   stippleCoord = 0.0; \n"
243      "   EmitVertex(); \n"
244      "   // Emit second vertex \n"
245      "   gl_FrontColor = gl_FrontColorIn[1]; \n"
246      "   gl_Position = pos1; \n"
247      "   stippleCoord = len / StippleFactor / 32.0; // Note: not 16, see above \n"
248      "   EmitVertex(); \n"
249      "} \n";
250
251   if (!ShadersSupported())
252      exit(1);
253
254   if (!glutExtensionSupported("GL_ARB_geometry_shader4")) {
255      fprintf(stderr, "Sorry, GL_ARB_geometry_shader4 is not supported.\n");
256      exit(1);
257   }
258
259   VertShader = CompileShaderText(GL_VERTEX_SHADER, vertShaderText);
260   FragShader = CompileShaderText(GL_FRAGMENT_SHADER, fragShaderText);
261   GeomShader = CompileShaderText(GL_GEOMETRY_SHADER_ARB, geomShaderText);
262   assert(GeomShader);
263
264   Program = LinkShaders3(VertShader, GeomShader, FragShader);
265   assert(Program);
266   CheckError(__LINE__);
267
268   /*
269    * The geometry shader accepts lines and produces lines.
270    */
271   glProgramParameteriARB(Program, GL_GEOMETRY_INPUT_TYPE_ARB,
272                          GL_LINES);
273   glProgramParameteriARB(Program, GL_GEOMETRY_OUTPUT_TYPE_ARB,
274                          GL_LINE_STRIP);
275   glProgramParameteriARB(Program, GL_GEOMETRY_VERTICES_OUT_ARB, 4);
276   CheckError(__LINE__);
277
278   glLinkProgramARB(Program);
279
280   /* check link */
281   {
282      GLint stat;
283      GetProgramiv(Program, GL_LINK_STATUS, &stat);
284      if (!stat) {
285         GLchar log[1000];
286         GLsizei len;
287         GetProgramInfoLog(Program, 1000, &len, log);
288         fprintf(stderr, "Shader link error:\n%s\n", log);
289      }
290   }
291
292   glUseProgram(Program);
293
294   uViewportSize = glGetUniformLocation(Program, "ViewportSize");
295   uStippleFactor = glGetUniformLocation(Program, "StippleFactor");
296   uStipplePattern = glGetUniformLocation(Program, "StipplePattern");
297
298   glUniform1f(uStippleFactor, StippleFactor);
299
300   glClearColor(0.3f, 0.3f, 0.3f, 0.0f);
301
302   printf("GL_RENDERER = %s\n",(const char *) glGetString(GL_RENDERER));
303
304   assert(glIsProgram(Program));
305   assert(glIsShader(FragShader));
306   assert(glIsShader(VertShader));
307   assert(glIsShader(GeomShader));
308
309
310   glLineStipple(StippleFactor, StipplePattern);
311   SetStippleUniform(StippleFactor, StipplePattern);
312
313   MakePointsVBO();
314}
315
316
317int
318main(int argc, char *argv[])
319{
320   glutInit(&argc, argv);
321
322   if (argc > 1) {
323      int n = atoi(argv[1]);
324      if (n > 0) {
325         NumPoints = n;
326      }
327      else {
328         printf("Invalid number of points\n");
329         return 1;
330      }
331   }
332
333   glutInitWindowSize(WinWidth, WinHeight);
334   glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
335   Win = glutCreateWindow(argv[0]);
336   glewInit();
337   glutReshapeFunc(Reshape);
338   glutKeyboardFunc(Key);
339   glutDisplayFunc(Redisplay);
340   if (Anim)
341      glutIdleFunc(Idle);
342
343   Init();
344   glutMainLoop();
345   return 0;
346}
347