1/**
2 * Test using a geometry shader to implement point sprites.
3 * XXX we should also demo point size attenuation.
4 *
5 * Brian Paul
6 * March 2011
7 */
8
9#include <assert.h>
10#include <string.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <math.h>
14#include <GL/glew.h>
15#include "glut_wrap.h"
16#include "shaderutil.h"
17
18static GLint WinWidth = 500, WinHeight = 500;
19static GLint Win = 0;
20static GLuint VertShader, GeomShader, FragShader, Program;
21static GLboolean Anim = GL_TRUE;
22static GLfloat Xrot = 0, Yrot = 0;
23static int uPointSize = -1, uInverseViewportSize = -1;
24
25static const int NumPoints = 50;
26static float Points[100][3];
27
28static const GLfloat Red[4] = {1, 0, 0, 1};
29static const GLfloat Green[4] = {0, 1, 0, 0};
30
31
32static void
33CheckError(int line)
34{
35   GLenum err = glGetError();
36   if (err) {
37      printf("GL Error %s (0x%x) at line %d\n",
38             gluErrorString(err), (int) err, line);
39   }
40}
41
42
43static void
44Redisplay(void)
45{
46   int i;
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   glBegin(GL_POINTS);
55   for (i = 0; i < NumPoints; i++) {
56      glVertex3fv(Points[i]);
57   }
58   glEnd();
59
60   glPopMatrix();
61
62   glutSwapBuffers();
63}
64
65
66static void
67Idle(void)
68{
69   int curTime = glutGet(GLUT_ELAPSED_TIME);
70   Xrot = curTime * 0.02;
71   Yrot = curTime * 0.05;
72   glutPostRedisplay();
73}
74
75
76static void
77Reshape(int width, int height)
78{
79   float ar = (float) width / height;
80   glViewport(0, 0, width, height);
81   glMatrixMode(GL_PROJECTION);
82   glLoadIdentity();
83   glFrustum(-ar, ar, -1, 1, 3, 25);
84   glMatrixMode(GL_MODELVIEW);
85   glLoadIdentity();
86   glTranslatef(0, 0, -10);
87
88   {
89      GLfloat viewport[4];
90      glGetFloatv(GL_VIEWPORT, viewport);
91      glUniform2f(uInverseViewportSize, 1.0F / viewport[2], 1.0F / viewport[3]);
92   }
93}
94
95
96static void
97CleanUp(void)
98{
99   glDeleteShader(FragShader);
100   glDeleteShader(VertShader);
101   glDeleteShader(GeomShader);
102   glDeleteProgram(Program);
103   glutDestroyWindow(Win);
104}
105
106
107static void
108Key(unsigned char key, int x, int y)
109{
110  (void) x;
111  (void) y;
112
113   switch(key) {
114   case ' ':
115   case 'a':
116      Anim = !Anim;
117      if (Anim) {
118         glutIdleFunc(Idle);
119      }
120      else
121         glutIdleFunc(NULL);
122      break;
123   case 27:
124      CleanUp();
125      exit(0);
126      break;
127   }
128   glutPostRedisplay();
129}
130
131
132static GLuint
133MakeTexture(void)
134{
135#define TEX_SIZE 32
136   GLubyte image[TEX_SIZE][TEX_SIZE][3];
137   GLuint i, j;
138   GLuint tex;
139
140   glGenTextures(1, &tex);
141   glBindTexture(GL_TEXTURE_2D, tex);
142
143   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
144   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
145
146   /* 'X' pattern */
147   for (i = 0; i < TEX_SIZE; i++) {
148      for (j = 0; j < TEX_SIZE; j++) {
149         int p1 = i - j, p2 = TEX_SIZE - 1 - i - j;
150         p1 = (p1 >= -2 && p1 <= 2);
151         p2 = (p2 >= -2 && p2 <= 2);
152         if (p1 || p2) {
153            image[i][j][0] = 255;
154            image[i][j][1] = 255;
155            image[i][j][2] = 255;
156         }
157         else {
158            image[i][j][0] = 50;
159            image[i][j][1] = 50;
160            image[i][j][2] = 50;
161         }
162      }
163   }
164
165   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, TEX_SIZE, TEX_SIZE, 0,
166                GL_RGB, GL_UNSIGNED_BYTE, image);
167
168   return tex;
169}
170
171
172static void
173MakePoints(void)
174{
175   int i;
176   for (i = 0; i < NumPoints; i++) {
177      Points[i][0] = ((rand() % 2000) - 1000.0) / 500.0;
178      Points[i][1] = ((rand() % 2000) - 1000.0) / 500.0;
179      Points[i][2] = ((rand() % 2000) - 1000.0) / 500.0;
180   }
181}
182
183static void
184Init(void)
185{
186   static const char *fragShaderText =
187      "uniform sampler2D tex; \n"
188      "void main() \n"
189      "{ \n"
190      "   gl_FragColor = texture2D(tex, gl_TexCoord[0].xy); \n"
191      "} \n";
192   static const char *vertShaderText =
193      "void main() \n"
194      "{ \n"
195      "   gl_FrontColor = gl_Color; \n"
196      "   gl_Position = ftransform(); \n"
197      "} \n";
198   static const char *geomShaderText =
199      "#version 120 \n"
200      "#extension GL_ARB_geometry_shader4: enable \n"
201      "uniform vec2 InverseViewportSize; \n"
202      "uniform float PointSize; \n"
203      "void main() \n"
204      "{ \n"
205      "   vec4 pos = gl_PositionIn[0]; \n"
206      "   vec2 d = vec2(PointSize * pos.w) * InverseViewportSize; \n"
207      "   gl_FrontColor = gl_FrontColorIn[0]; \n"
208      "   gl_TexCoord[0] = vec4(0, 0, 0, 1); \n"
209      "   gl_Position = pos + vec4(-d.x, -d.y, 0, 0); \n"
210      "   EmitVertex(); \n"
211      "   gl_TexCoord[0] = vec4(1, 0, 0, 1); \n"
212      "   gl_Position = pos + vec4( d.x, -d.y, 0, 0); \n"
213      "   EmitVertex(); \n"
214      "   gl_TexCoord[0] = vec4(0, 1, 0, 1); \n"
215      "   gl_Position = pos + vec4(-d.x,  d.y, 0, 0); \n"
216      "   EmitVertex(); \n"
217      "   gl_TexCoord[0] = vec4(1, 1, 0, 1); \n"
218      "   gl_Position = pos + vec4( d.x,  d.y, 0, 0); \n"
219      "   EmitVertex(); \n"
220      "} \n";
221
222   if (!ShadersSupported())
223      exit(1);
224
225   if (!glutExtensionSupported("GL_ARB_geometry_shader4")) {
226      fprintf(stderr, "Sorry, GL_ARB_geometry_shader4 is not supported.\n");
227      exit(1);
228   }
229
230   VertShader = CompileShaderText(GL_VERTEX_SHADER, vertShaderText);
231   FragShader = CompileShaderText(GL_FRAGMENT_SHADER, fragShaderText);
232   GeomShader = CompileShaderText(GL_GEOMETRY_SHADER_ARB, geomShaderText);
233   assert(GeomShader);
234
235   Program = LinkShaders3(VertShader, GeomShader, FragShader);
236   assert(Program);
237   CheckError(__LINE__);
238
239   /*
240    * The geometry shader will convert incoming points to quads (4-vertex
241    * triangle strips).
242    */
243   glProgramParameteriARB(Program, GL_GEOMETRY_INPUT_TYPE_ARB,
244                          GL_POINTS);
245   glProgramParameteriARB(Program, GL_GEOMETRY_OUTPUT_TYPE_ARB,
246                          GL_TRIANGLE_STRIP);
247   glProgramParameteriARB(Program,GL_GEOMETRY_VERTICES_OUT_ARB, 4);
248   CheckError(__LINE__);
249
250   glLinkProgramARB(Program);
251
252   /* check link */
253   {
254      GLint stat;
255      GetProgramiv(Program, GL_LINK_STATUS, &stat);
256      if (!stat) {
257         GLchar log[1000];
258         GLsizei len;
259         GetProgramInfoLog(Program, 1000, &len, log);
260         fprintf(stderr, "Shader link error:\n%s\n", log);
261      }
262   }
263
264   CheckError(__LINE__);
265
266   glUseProgram(Program);
267   CheckError(__LINE__);
268
269   uInverseViewportSize = glGetUniformLocation(Program, "InverseViewportSize");
270   uPointSize = glGetUniformLocation(Program, "PointSize");
271
272   glUniform1f(uPointSize, 24.0);
273
274   glClearColor(0.3f, 0.3f, 0.3f, 0.0f);
275
276   printf("GL_RENDERER = %s\n",(const char *) glGetString(GL_RENDERER));
277
278   assert(glIsProgram(Program));
279   assert(glIsShader(FragShader));
280   assert(glIsShader(VertShader));
281   assert(glIsShader(GeomShader));
282
283   glEnable(GL_DEPTH_TEST);
284
285   MakeTexture();
286   MakePoints();
287}
288
289
290int
291main(int argc, char *argv[])
292{
293   glutInit(&argc, argv);
294   glutInitWindowSize(WinWidth, WinHeight);
295   glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
296   Win = glutCreateWindow(argv[0]);
297   glewInit();
298   glutReshapeFunc(Reshape);
299   glutKeyboardFunc(Key);
300   glutDisplayFunc(Redisplay);
301   if (Anim)
302      glutIdleFunc(Idle);
303
304   Init();
305   glutMainLoop();
306   return 0;
307}
308