132001f49Smrg/**
232001f49Smrg * Test using a geometry shader to implement polygon outlining.
332001f49Smrg *
432001f49Smrg * Based on the technique "Single-Pass Wireframe Rendering" by Andreas
532001f49Smrg * Bærentzen, Steen Lund Nielsen, Mikkel Gjael, Bent D. Larsen & Niels
632001f49Smrg * Jaergen Christensen, SIGGRAPH 2006
732001f49Smrg *
832001f49Smrg * Brian Paul
932001f49Smrg * May 2012
1032001f49Smrg */
1132001f49Smrg
1232001f49Smrg#include <assert.h>
1332001f49Smrg#include <string.h>
1432001f49Smrg#include <stdio.h>
1532001f49Smrg#include <stdlib.h>
1632001f49Smrg#include <math.h>
1732001f49Smrg#include <GL/glew.h>
1832001f49Smrg#include "glut_wrap.h"
1932001f49Smrg#include "shaderutil.h"
2032001f49Smrg#include "trackball.h"
2132001f49Smrg
2232001f49Smrgstatic GLint WinWidth = 500, WinHeight = 500;
2332001f49Smrgstatic GLint Win = 0;
2432001f49Smrgstatic GLuint VertShader, GeomShader, FragShader, Program;
2532001f49Smrgstatic GLboolean Anim = GL_TRUE;
2632001f49Smrgstatic int uViewportSize = -1;
2732001f49Smrg
2832001f49Smrgstatic const GLfloat Orange[4] = {1.0, 0.6, 0.0, 1};
2932001f49Smrg
3032001f49Smrgstatic float CurQuat[4] = { 0, 0, 0, 1 };
3132001f49Smrgstatic GLboolean ButtonDown = GL_FALSE;
3232001f49Smrgstatic GLint ButtonX, ButtonY;
3332001f49Smrg
3432001f49Smrg
3532001f49Smrgstatic void
3632001f49SmrgCheckError(int line)
3732001f49Smrg{
3832001f49Smrg   GLenum err = glGetError();
3932001f49Smrg   if (err) {
4032001f49Smrg      printf("GL Error %s (0x%x) at line %d\n",
4132001f49Smrg             gluErrorString(err), (int) err, line);
4232001f49Smrg   }
4332001f49Smrg}
4432001f49Smrg
4532001f49Smrg
4632001f49Smrgstatic void
4732001f49SmrgRedisplay(void)
4832001f49Smrg{
4932001f49Smrg   GLfloat rot[4][4];
5032001f49Smrg
5132001f49Smrg   glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
5232001f49Smrg
5332001f49Smrg   glColor4fv(Orange);
5432001f49Smrg
5532001f49Smrg   glPushMatrix();
5632001f49Smrg   build_rotmatrix(rot, CurQuat);
5732001f49Smrg   glMultMatrixf(&rot[0][0]);
5832001f49Smrg
5932001f49Smrg   if (0)
6032001f49Smrg      glutSolidDodecahedron();
6132001f49Smrg   else
6232001f49Smrg      glutSolidSphere(2, 30, 20);
6332001f49Smrg
6432001f49Smrg   glPopMatrix();
6532001f49Smrg
6632001f49Smrg   glutSwapBuffers();
6732001f49Smrg}
6832001f49Smrg
6932001f49Smrg
7032001f49Smrgstatic void
7132001f49SmrgIdle(void)
7232001f49Smrg{
7332001f49Smrg   static const float yAxis[3] = {0, 1, 0};
7432001f49Smrg   static double t0 = -1.;
7532001f49Smrg   float quat[4];
7632001f49Smrg   double dt, t = glutGet(GLUT_ELAPSED_TIME) / 2000.0;
7732001f49Smrg   if (t0 < 0.0)
7832001f49Smrg      t0 = t;
7932001f49Smrg   dt = t - t0;
8032001f49Smrg   t0 = t;
8132001f49Smrg
8232001f49Smrg   axis_to_quat(yAxis, 2.0 * dt, quat);
8332001f49Smrg   add_quats(quat, CurQuat, CurQuat);
8432001f49Smrg
8532001f49Smrg   glutPostRedisplay();
8632001f49Smrg}
8732001f49Smrg
8832001f49Smrg
8932001f49Smrgstatic void
9032001f49SmrgReshape(int width, int height)
9132001f49Smrg{
9232001f49Smrg   float ar = (float) width / height;
9332001f49Smrg   WinWidth = width;
9432001f49Smrg   WinHeight = height;
9532001f49Smrg   glViewport(0, 0, width, height);
9632001f49Smrg   glMatrixMode(GL_PROJECTION);
9732001f49Smrg   glLoadIdentity();
9832001f49Smrg   glFrustum(-ar, ar, -1, 1, 3, 25);
9932001f49Smrg   glMatrixMode(GL_MODELVIEW);
10032001f49Smrg   glLoadIdentity();
10132001f49Smrg   glTranslatef(0, 0, -10);
10232001f49Smrg
10332001f49Smrg   /* pass viewport dims to the shader */
10432001f49Smrg   {
10532001f49Smrg      GLfloat viewport[4];
10632001f49Smrg      glGetFloatv(GL_VIEWPORT, viewport);
10732001f49Smrg      glUniform2f(uViewportSize, viewport[2], viewport[3]);
10832001f49Smrg   }
10932001f49Smrg}
11032001f49Smrg
11132001f49Smrg
11232001f49Smrgstatic void
11332001f49SmrgMouseMotion(int x, int y)
11432001f49Smrg{
11532001f49Smrg   if (ButtonDown) {
11632001f49Smrg      float x0 = (2.0 * ButtonX - WinWidth) / WinWidth;
11732001f49Smrg      float y0 = (WinHeight - 2.0 * ButtonY) / WinHeight;
11832001f49Smrg      float x1 = (2.0 * x - WinWidth) / WinWidth;
11932001f49Smrg      float y1 = (WinHeight - 2.0 * y) / WinHeight;
12032001f49Smrg      float q[4];
12132001f49Smrg
12232001f49Smrg      trackball(q, x0, y0, x1, y1);
12332001f49Smrg      ButtonX = x;
12432001f49Smrg      ButtonY = y;
12532001f49Smrg      add_quats(q, CurQuat, CurQuat);
12632001f49Smrg
12732001f49Smrg      glutPostRedisplay();
12832001f49Smrg   }
12932001f49Smrg}
13032001f49Smrg
13132001f49Smrg
13232001f49Smrgstatic void
13332001f49SmrgMouseButton(int button, int state, int x, int y)
13432001f49Smrg{
13532001f49Smrg  if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
13632001f49Smrg     ButtonDown = GL_TRUE;
13732001f49Smrg     ButtonX = x;
13832001f49Smrg     ButtonY = y;
13932001f49Smrg  }
14032001f49Smrg  else if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
14132001f49Smrg     ButtonDown = GL_FALSE;
14232001f49Smrg  }
14332001f49Smrg}
14432001f49Smrg
14532001f49Smrg
14632001f49Smrgstatic void
14732001f49SmrgCleanUp(void)
14832001f49Smrg{
14932001f49Smrg   glDeleteShader(FragShader);
15032001f49Smrg   glDeleteShader(VertShader);
15132001f49Smrg   glDeleteShader(GeomShader);
15232001f49Smrg   glDeleteProgram(Program);
15332001f49Smrg   glutDestroyWindow(Win);
15432001f49Smrg}
15532001f49Smrg
15632001f49Smrg
15732001f49Smrgstatic void
15832001f49SmrgKey(unsigned char key, int x, int y)
15932001f49Smrg{
16032001f49Smrg  (void) x;
16132001f49Smrg  (void) y;
16232001f49Smrg
16332001f49Smrg   switch(key) {
16432001f49Smrg   case ' ':
16532001f49Smrg   case 'a':
16632001f49Smrg      Anim = !Anim;
16732001f49Smrg      if (Anim) {
16832001f49Smrg         glutIdleFunc(Idle);
16932001f49Smrg      }
17032001f49Smrg      else
17132001f49Smrg         glutIdleFunc(NULL);
17232001f49Smrg      break;
17332001f49Smrg   case 27:
17432001f49Smrg      CleanUp();
17532001f49Smrg      exit(0);
17632001f49Smrg      break;
17732001f49Smrg   }
17832001f49Smrg   glutPostRedisplay();
17932001f49Smrg}
18032001f49Smrg
18132001f49Smrg
18232001f49Smrgstatic void
18332001f49SmrgInit(void)
18432001f49Smrg{
18532001f49Smrg   static const char *vertShaderText =
18632001f49Smrg      "#version 120 \n"
18732001f49Smrg      "void main() \n"
18832001f49Smrg      "{ \n"
18932001f49Smrg      "   gl_FrontColor = gl_Color; \n"
19032001f49Smrg      "   gl_Position = ftransform(); \n"
19132001f49Smrg      "} \n";
19232001f49Smrg   static const char *geomShaderText =
19332001f49Smrg      "#version 120 \n"
19432001f49Smrg      "#extension GL_ARB_geometry_shader4: enable \n"
19532001f49Smrg      "uniform vec2 ViewportSize; \n"
19632001f49Smrg      "varying vec2 Vert0, Vert1, Vert2; \n"
19732001f49Smrg      "\n"
19832001f49Smrg      "// Transform NDC coord to window coord \n"
19932001f49Smrg      "vec2 vpxform(vec4 p) \n"
20032001f49Smrg      "{ \n"
20132001f49Smrg      "   return (p.xy / p.w + 1.0) * 0.5 * ViewportSize; \n"
20232001f49Smrg      "} \n"
20332001f49Smrg      "\n"
20432001f49Smrg      "void main() \n"
20532001f49Smrg      "{ \n"
20632001f49Smrg      "   gl_FrontColor = gl_FrontColorIn[0]; \n"
20732001f49Smrg      "   Vert0 = vpxform(gl_PositionIn[0]); \n"
20832001f49Smrg      "   Vert1 = vpxform(gl_PositionIn[1]); \n"
20932001f49Smrg      "   Vert2 = vpxform(gl_PositionIn[2]); \n"
21032001f49Smrg      "   gl_Position = gl_PositionIn[0]; \n"
21132001f49Smrg      "   EmitVertex(); \n"
21232001f49Smrg      "   gl_Position = gl_PositionIn[1]; \n"
21332001f49Smrg      "   EmitVertex(); \n"
21432001f49Smrg      "   gl_Position = gl_PositionIn[2]; \n"
21532001f49Smrg      "   EmitVertex(); \n"
21632001f49Smrg      "} \n";
21732001f49Smrg   static const char *fragShaderText =
21832001f49Smrg      "#version 120 \n"
21932001f49Smrg      "#define LINE_WIDTH 2.5 \n"
22032001f49Smrg      "varying vec2 Vert0, Vert1, Vert2; \n"
22132001f49Smrg      "// Compute distance from a point to a line \n"
22232001f49Smrg      "float point_line_dist(vec2 p, vec2 v1, vec2 v2) \n"
22332001f49Smrg      "{ \n"
22432001f49Smrg      "   float s = (v2.x - v1.x) * (v1.y - p.y) - (v1.x - p.x) * (v2.y - v1.y); \n"
22532001f49Smrg      "   float t = length(v2 - v1); \n"
22632001f49Smrg      "   return abs(s) / t; \n"
22732001f49Smrg      "} \n"
22832001f49Smrg      "\n"
22932001f49Smrg      "void main() \n"
23032001f49Smrg      "{ \n"
23132001f49Smrg      "   float d0 = point_line_dist(gl_FragCoord.xy, Vert0, Vert1); \n"
23232001f49Smrg      "   float d1 = point_line_dist(gl_FragCoord.xy, Vert1, Vert2); \n"
23332001f49Smrg      "   float d2 = point_line_dist(gl_FragCoord.xy, Vert2, Vert0); \n"
23432001f49Smrg      "   float m = min(d0, min(d1, d2)); \n"
23532001f49Smrg      "   gl_FragColor = gl_Color * smoothstep(0.0, LINE_WIDTH, m); \n"
23632001f49Smrg      "} \n";
23732001f49Smrg
23832001f49Smrg   if (!ShadersSupported())
23932001f49Smrg      exit(1);
24032001f49Smrg
24132001f49Smrg   if (!glutExtensionSupported("GL_ARB_geometry_shader4")) {
24232001f49Smrg      fprintf(stderr, "Sorry, GL_ARB_geometry_shader4 is not supported.\n");
24332001f49Smrg      exit(1);
24432001f49Smrg   }
24532001f49Smrg
24632001f49Smrg   VertShader = CompileShaderText(GL_VERTEX_SHADER, vertShaderText);
24732001f49Smrg   FragShader = CompileShaderText(GL_FRAGMENT_SHADER, fragShaderText);
24832001f49Smrg   GeomShader = CompileShaderText(GL_GEOMETRY_SHADER_ARB, geomShaderText);
24932001f49Smrg
25032001f49Smrg   Program = LinkShaders3(VertShader, GeomShader, FragShader);
25132001f49Smrg   assert(Program);
25232001f49Smrg   CheckError(__LINE__);
25332001f49Smrg
25432001f49Smrg   /*
25532001f49Smrg    * The geometry shader will receive and emit triangles.
25632001f49Smrg    */
25732001f49Smrg   glProgramParameteriARB(Program, GL_GEOMETRY_INPUT_TYPE_ARB,
25832001f49Smrg                          GL_TRIANGLES);
25932001f49Smrg   glProgramParameteriARB(Program, GL_GEOMETRY_OUTPUT_TYPE_ARB,
26032001f49Smrg                          GL_TRIANGLE_STRIP);
26132001f49Smrg   glProgramParameteriARB(Program,GL_GEOMETRY_VERTICES_OUT_ARB, 3);
26232001f49Smrg   CheckError(__LINE__);
26332001f49Smrg
26432001f49Smrg   /* relink */
26532001f49Smrg   glLinkProgramARB(Program);
26632001f49Smrg
26732001f49Smrg   assert(glIsProgram(Program));
26832001f49Smrg   assert(glIsShader(FragShader));
26932001f49Smrg   assert(glIsShader(VertShader));
27032001f49Smrg   assert(glIsShader(GeomShader));
27132001f49Smrg
27232001f49Smrg   glUseProgram(Program);
27332001f49Smrg
27432001f49Smrg   uViewportSize = glGetUniformLocation(Program, "ViewportSize");
27532001f49Smrg
27632001f49Smrg   glClearColor(0.3f, 0.3f, 0.3f, 0.0f);
27732001f49Smrg   glEnable(GL_DEPTH_TEST);
27832001f49Smrg
27932001f49Smrg   printf("GL_RENDERER = %s\n",(const char *) glGetString(GL_RENDERER));
28032001f49Smrg}
28132001f49Smrg
28232001f49Smrg
28332001f49Smrgint
28432001f49Smrgmain(int argc, char *argv[])
28532001f49Smrg{
28632001f49Smrg   glutInit(&argc, argv);
28732001f49Smrg   glutInitWindowSize(WinWidth, WinHeight);
28832001f49Smrg   glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
28932001f49Smrg   Win = glutCreateWindow(argv[0]);
29032001f49Smrg   glewInit();
29132001f49Smrg   glutReshapeFunc(Reshape);
29232001f49Smrg   glutKeyboardFunc(Key);
29332001f49Smrg   glutDisplayFunc(Redisplay);
29432001f49Smrg   glutMotionFunc(MouseMotion);
29532001f49Smrg   glutMouseFunc(MouseButton);
29632001f49Smrg   if (Anim)
29732001f49Smrg      glutIdleFunc(Idle);
29832001f49Smrg
29932001f49Smrg   Init();
30032001f49Smrg   glutMainLoop();
30132001f49Smrg   return 0;
30232001f49Smrg}
303