132001f49Smrg/**
232001f49Smrg * Test using a geometry shader to implement point sprites.
332001f49Smrg * XXX we should also demo point size attenuation.
432001f49Smrg *
532001f49Smrg * Brian Paul
632001f49Smrg * March 2011
732001f49Smrg */
832001f49Smrg
932001f49Smrg#include <assert.h>
1032001f49Smrg#include <string.h>
1132001f49Smrg#include <stdio.h>
1232001f49Smrg#include <stdlib.h>
1332001f49Smrg#include <math.h>
1432001f49Smrg#include <GL/glew.h>
1532001f49Smrg#include "glut_wrap.h"
1632001f49Smrg#include "shaderutil.h"
1732001f49Smrg
1832001f49Smrgstatic GLint WinWidth = 500, WinHeight = 500;
1932001f49Smrgstatic GLint Win = 0;
2032001f49Smrgstatic GLuint VertShader, GeomShader, FragShader, Program;
2132001f49Smrgstatic GLboolean Anim = GL_TRUE;
2232001f49Smrgstatic GLfloat Xrot = 0, Yrot = 0;
2332001f49Smrgstatic int uPointSize = -1, uInverseViewportSize = -1;
2432001f49Smrg
2532001f49Smrgstatic const int NumPoints = 50;
2632001f49Smrgstatic float Points[100][3];
2732001f49Smrg
2832001f49Smrgstatic const GLfloat Red[4] = {1, 0, 0, 1};
2932001f49Smrgstatic const GLfloat Green[4] = {0, 1, 0, 0};
3032001f49Smrg
3132001f49Smrg
3232001f49Smrgstatic void
3332001f49SmrgCheckError(int line)
3432001f49Smrg{
3532001f49Smrg   GLenum err = glGetError();
3632001f49Smrg   if (err) {
3732001f49Smrg      printf("GL Error %s (0x%x) at line %d\n",
3832001f49Smrg             gluErrorString(err), (int) err, line);
3932001f49Smrg   }
4032001f49Smrg}
4132001f49Smrg
4232001f49Smrg
4332001f49Smrgstatic void
4432001f49SmrgRedisplay(void)
4532001f49Smrg{
4632001f49Smrg   int i;
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   glBegin(GL_POINTS);
5532001f49Smrg   for (i = 0; i < NumPoints; i++) {
5632001f49Smrg      glVertex3fv(Points[i]);
5732001f49Smrg   }
5832001f49Smrg   glEnd();
5932001f49Smrg
6032001f49Smrg   glPopMatrix();
6132001f49Smrg
6232001f49Smrg   glutSwapBuffers();
6332001f49Smrg}
6432001f49Smrg
6532001f49Smrg
6632001f49Smrgstatic void
6732001f49SmrgIdle(void)
6832001f49Smrg{
6932001f49Smrg   int curTime = glutGet(GLUT_ELAPSED_TIME);
7032001f49Smrg   Xrot = curTime * 0.02;
7132001f49Smrg   Yrot = curTime * 0.05;
7232001f49Smrg   glutPostRedisplay();
7332001f49Smrg}
7432001f49Smrg
7532001f49Smrg
7632001f49Smrgstatic void
7732001f49SmrgReshape(int width, int height)
7832001f49Smrg{
7932001f49Smrg   float ar = (float) width / height;
8032001f49Smrg   glViewport(0, 0, width, height);
8132001f49Smrg   glMatrixMode(GL_PROJECTION);
8232001f49Smrg   glLoadIdentity();
8332001f49Smrg   glFrustum(-ar, ar, -1, 1, 3, 25);
8432001f49Smrg   glMatrixMode(GL_MODELVIEW);
8532001f49Smrg   glLoadIdentity();
8632001f49Smrg   glTranslatef(0, 0, -10);
8732001f49Smrg
8832001f49Smrg   {
8932001f49Smrg      GLfloat viewport[4];
9032001f49Smrg      glGetFloatv(GL_VIEWPORT, viewport);
9132001f49Smrg      glUniform2f(uInverseViewportSize, 1.0F / viewport[2], 1.0F / viewport[3]);
9232001f49Smrg   }
9332001f49Smrg}
9432001f49Smrg
9532001f49Smrg
9632001f49Smrgstatic void
9732001f49SmrgCleanUp(void)
9832001f49Smrg{
9932001f49Smrg   glDeleteShader(FragShader);
10032001f49Smrg   glDeleteShader(VertShader);
10132001f49Smrg   glDeleteShader(GeomShader);
10232001f49Smrg   glDeleteProgram(Program);
10332001f49Smrg   glutDestroyWindow(Win);
10432001f49Smrg}
10532001f49Smrg
10632001f49Smrg
10732001f49Smrgstatic void
10832001f49SmrgKey(unsigned char key, int x, int y)
10932001f49Smrg{
11032001f49Smrg  (void) x;
11132001f49Smrg  (void) y;
11232001f49Smrg
11332001f49Smrg   switch(key) {
11432001f49Smrg   case ' ':
11532001f49Smrg   case 'a':
11632001f49Smrg      Anim = !Anim;
11732001f49Smrg      if (Anim) {
11832001f49Smrg         glutIdleFunc(Idle);
11932001f49Smrg      }
12032001f49Smrg      else
12132001f49Smrg         glutIdleFunc(NULL);
12232001f49Smrg      break;
12332001f49Smrg   case 27:
12432001f49Smrg      CleanUp();
12532001f49Smrg      exit(0);
12632001f49Smrg      break;
12732001f49Smrg   }
12832001f49Smrg   glutPostRedisplay();
12932001f49Smrg}
13032001f49Smrg
13132001f49Smrg
13232001f49Smrgstatic GLuint
13332001f49SmrgMakeTexture(void)
13432001f49Smrg{
13532001f49Smrg#define TEX_SIZE 32
13632001f49Smrg   GLubyte image[TEX_SIZE][TEX_SIZE][3];
13732001f49Smrg   GLuint i, j;
13832001f49Smrg   GLuint tex;
13932001f49Smrg
14032001f49Smrg   glGenTextures(1, &tex);
14132001f49Smrg   glBindTexture(GL_TEXTURE_2D, tex);
14232001f49Smrg
14332001f49Smrg   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
14432001f49Smrg   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
14532001f49Smrg
14632001f49Smrg   /* 'X' pattern */
14732001f49Smrg   for (i = 0; i < TEX_SIZE; i++) {
14832001f49Smrg      for (j = 0; j < TEX_SIZE; j++) {
14932001f49Smrg         int p1 = i - j, p2 = TEX_SIZE - 1 - i - j;
15032001f49Smrg         p1 = (p1 >= -2 && p1 <= 2);
15132001f49Smrg         p2 = (p2 >= -2 && p2 <= 2);
15232001f49Smrg         if (p1 || p2) {
15332001f49Smrg            image[i][j][0] = 255;
15432001f49Smrg            image[i][j][1] = 255;
15532001f49Smrg            image[i][j][2] = 255;
15632001f49Smrg         }
15732001f49Smrg         else {
15832001f49Smrg            image[i][j][0] = 50;
15932001f49Smrg            image[i][j][1] = 50;
16032001f49Smrg            image[i][j][2] = 50;
16132001f49Smrg         }
16232001f49Smrg      }
16332001f49Smrg   }
16432001f49Smrg
16532001f49Smrg   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, TEX_SIZE, TEX_SIZE, 0,
16632001f49Smrg                GL_RGB, GL_UNSIGNED_BYTE, image);
16732001f49Smrg
16832001f49Smrg   return tex;
16932001f49Smrg}
17032001f49Smrg
17132001f49Smrg
17232001f49Smrgstatic void
17332001f49SmrgMakePoints(void)
17432001f49Smrg{
17532001f49Smrg   int i;
17632001f49Smrg   for (i = 0; i < NumPoints; i++) {
17732001f49Smrg      Points[i][0] = ((rand() % 2000) - 1000.0) / 500.0;
17832001f49Smrg      Points[i][1] = ((rand() % 2000) - 1000.0) / 500.0;
17932001f49Smrg      Points[i][2] = ((rand() % 2000) - 1000.0) / 500.0;
18032001f49Smrg   }
18132001f49Smrg}
18232001f49Smrg
18332001f49Smrgstatic void
18432001f49SmrgInit(void)
18532001f49Smrg{
18632001f49Smrg   static const char *fragShaderText =
18732001f49Smrg      "uniform sampler2D tex; \n"
18832001f49Smrg      "void main() \n"
18932001f49Smrg      "{ \n"
19032001f49Smrg      "   gl_FragColor = texture2D(tex, gl_TexCoord[0].xy); \n"
19132001f49Smrg      "} \n";
19232001f49Smrg   static const char *vertShaderText =
19332001f49Smrg      "void main() \n"
19432001f49Smrg      "{ \n"
19532001f49Smrg      "   gl_FrontColor = gl_Color; \n"
19632001f49Smrg      "   gl_Position = ftransform(); \n"
19732001f49Smrg      "} \n";
19832001f49Smrg   static const char *geomShaderText =
19932001f49Smrg      "#version 120 \n"
20032001f49Smrg      "#extension GL_ARB_geometry_shader4: enable \n"
20132001f49Smrg      "uniform vec2 InverseViewportSize; \n"
20232001f49Smrg      "uniform float PointSize; \n"
20332001f49Smrg      "void main() \n"
20432001f49Smrg      "{ \n"
20532001f49Smrg      "   vec4 pos = gl_PositionIn[0]; \n"
20632001f49Smrg      "   vec2 d = vec2(PointSize * pos.w) * InverseViewportSize; \n"
20732001f49Smrg      "   gl_FrontColor = gl_FrontColorIn[0]; \n"
20832001f49Smrg      "   gl_TexCoord[0] = vec4(0, 0, 0, 1); \n"
20932001f49Smrg      "   gl_Position = pos + vec4(-d.x, -d.y, 0, 0); \n"
21032001f49Smrg      "   EmitVertex(); \n"
21132001f49Smrg      "   gl_TexCoord[0] = vec4(1, 0, 0, 1); \n"
21232001f49Smrg      "   gl_Position = pos + vec4( d.x, -d.y, 0, 0); \n"
21332001f49Smrg      "   EmitVertex(); \n"
21432001f49Smrg      "   gl_TexCoord[0] = vec4(0, 1, 0, 1); \n"
21532001f49Smrg      "   gl_Position = pos + vec4(-d.x,  d.y, 0, 0); \n"
21632001f49Smrg      "   EmitVertex(); \n"
21732001f49Smrg      "   gl_TexCoord[0] = vec4(1, 1, 0, 1); \n"
21832001f49Smrg      "   gl_Position = pos + vec4( d.x,  d.y, 0, 0); \n"
21932001f49Smrg      "   EmitVertex(); \n"
22032001f49Smrg      "} \n";
22132001f49Smrg
22232001f49Smrg   if (!ShadersSupported())
22332001f49Smrg      exit(1);
22432001f49Smrg
22532001f49Smrg   if (!glutExtensionSupported("GL_ARB_geometry_shader4")) {
22632001f49Smrg      fprintf(stderr, "Sorry, GL_ARB_geometry_shader4 is not supported.\n");
22732001f49Smrg      exit(1);
22832001f49Smrg   }
22932001f49Smrg
23032001f49Smrg   VertShader = CompileShaderText(GL_VERTEX_SHADER, vertShaderText);
23132001f49Smrg   FragShader = CompileShaderText(GL_FRAGMENT_SHADER, fragShaderText);
23232001f49Smrg   GeomShader = CompileShaderText(GL_GEOMETRY_SHADER_ARB, geomShaderText);
23332001f49Smrg   assert(GeomShader);
23432001f49Smrg
23532001f49Smrg   Program = LinkShaders3(VertShader, GeomShader, FragShader);
23632001f49Smrg   assert(Program);
23732001f49Smrg   CheckError(__LINE__);
23832001f49Smrg
23932001f49Smrg   /*
24032001f49Smrg    * The geometry shader will convert incoming points to quads (4-vertex
24132001f49Smrg    * triangle strips).
24232001f49Smrg    */
24332001f49Smrg   glProgramParameteriARB(Program, GL_GEOMETRY_INPUT_TYPE_ARB,
24432001f49Smrg                          GL_POINTS);
24532001f49Smrg   glProgramParameteriARB(Program, GL_GEOMETRY_OUTPUT_TYPE_ARB,
24632001f49Smrg                          GL_TRIANGLE_STRIP);
24732001f49Smrg   glProgramParameteriARB(Program,GL_GEOMETRY_VERTICES_OUT_ARB, 4);
24832001f49Smrg   CheckError(__LINE__);
24932001f49Smrg
25032001f49Smrg   glLinkProgramARB(Program);
25132001f49Smrg
25232001f49Smrg   /* check link */
25332001f49Smrg   {
25432001f49Smrg      GLint stat;
25532001f49Smrg      GetProgramiv(Program, GL_LINK_STATUS, &stat);
25632001f49Smrg      if (!stat) {
25732001f49Smrg         GLchar log[1000];
25832001f49Smrg         GLsizei len;
25932001f49Smrg         GetProgramInfoLog(Program, 1000, &len, log);
26032001f49Smrg         fprintf(stderr, "Shader link error:\n%s\n", log);
26132001f49Smrg      }
26232001f49Smrg   }
26332001f49Smrg
26432001f49Smrg   CheckError(__LINE__);
26532001f49Smrg
26632001f49Smrg   glUseProgram(Program);
26732001f49Smrg   CheckError(__LINE__);
26832001f49Smrg
26932001f49Smrg   uInverseViewportSize = glGetUniformLocation(Program, "InverseViewportSize");
27032001f49Smrg   uPointSize = glGetUniformLocation(Program, "PointSize");
27132001f49Smrg
27232001f49Smrg   glUniform1f(uPointSize, 24.0);
27332001f49Smrg
27432001f49Smrg   glClearColor(0.3f, 0.3f, 0.3f, 0.0f);
27532001f49Smrg
27632001f49Smrg   printf("GL_RENDERER = %s\n",(const char *) glGetString(GL_RENDERER));
27732001f49Smrg
27832001f49Smrg   assert(glIsProgram(Program));
27932001f49Smrg   assert(glIsShader(FragShader));
28032001f49Smrg   assert(glIsShader(VertShader));
28132001f49Smrg   assert(glIsShader(GeomShader));
28232001f49Smrg
28332001f49Smrg   glEnable(GL_DEPTH_TEST);
28432001f49Smrg
28532001f49Smrg   MakeTexture();
28632001f49Smrg   MakePoints();
28732001f49Smrg}
28832001f49Smrg
28932001f49Smrg
29032001f49Smrgint
29132001f49Smrgmain(int argc, char *argv[])
29232001f49Smrg{
29332001f49Smrg   glutInit(&argc, argv);
29432001f49Smrg   glutInitWindowSize(WinWidth, WinHeight);
29532001f49Smrg   glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
29632001f49Smrg   Win = glutCreateWindow(argv[0]);
29732001f49Smrg   glewInit();
29832001f49Smrg   glutReshapeFunc(Reshape);
29932001f49Smrg   glutKeyboardFunc(Key);
30032001f49Smrg   glutDisplayFunc(Redisplay);
30132001f49Smrg   if (Anim)
30232001f49Smrg      glutIdleFunc(Idle);
30332001f49Smrg
30432001f49Smrg   Init();
30532001f49Smrg   glutMainLoop();
30632001f49Smrg   return 0;
30732001f49Smrg}
308