1 /**
2  * Test using a geometry shader to implement polygon outlining.
3  *
4  * Based on the technique "Single-Pass Wireframe Rendering" by Andreas
5  * Bærentzen, Steen Lund Nielsen, Mikkel Gjael, Bent D. Larsen & Niels
6  * Jaergen Christensen, SIGGRAPH 2006
7  *
8  * Brian Paul
9  * May 2012
10  */
11 
12 #include <assert.h>
13 #include <string.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <math.h>
17 #include <glad/glad.h>
18 #include "glut_wrap.h"
19 #include "shaderutil.h"
20 #include "trackball.h"
21 
22 static GLint WinWidth = 500, WinHeight = 500;
23 static GLint Win = 0;
24 static GLuint VertShader, GeomShader, FragShader, Program;
25 static GLboolean Anim = GL_TRUE;
26 static int uViewportSize = -1;
27 
28 static const GLfloat Orange[4] = {1.0, 0.6, 0.0, 1};
29 
30 static float CurQuat[4] = { 0, 0, 0, 1 };
31 static GLboolean ButtonDown = GL_FALSE;
32 static GLint ButtonX, ButtonY;
33 
34 
35 static void
CheckError(int line)36 CheckError(int line)
37 {
38    GLenum err = glGetError();
39    if (err) {
40       printf("GL Error %s (0x%x) at line %d\n",
41              gluErrorString(err), (int) err, line);
42    }
43 }
44 
45 
46 static void
Redisplay(void)47 Redisplay(void)
48 {
49    GLfloat rot[4][4];
50 
51    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
52 
53    glColor4fv(Orange);
54 
55    glPushMatrix();
56    build_rotmatrix(rot, CurQuat);
57    glMultMatrixf(&rot[0][0]);
58 
59    if (0)
60       glutSolidDodecahedron();
61    else
62       glutSolidSphere(2, 30, 20);
63 
64    glPopMatrix();
65 
66    glutSwapBuffers();
67 }
68 
69 
70 static void
Idle(void)71 Idle(void)
72 {
73    static const float yAxis[3] = {0, 1, 0};
74    static double t0 = -1.;
75    float quat[4];
76    double dt, t = glutGet(GLUT_ELAPSED_TIME) / 2000.0;
77    if (t0 < 0.0)
78       t0 = t;
79    dt = t - t0;
80    t0 = t;
81 
82    axis_to_quat(yAxis, 2.0 * dt, quat);
83    add_quats(quat, CurQuat, CurQuat);
84 
85    glutPostRedisplay();
86 }
87 
88 
89 static void
Reshape(int width,int height)90 Reshape(int width, int height)
91 {
92    float ar = (float) width / height;
93    WinWidth = width;
94    WinHeight = height;
95    glViewport(0, 0, width, height);
96    glMatrixMode(GL_PROJECTION);
97    glLoadIdentity();
98    glFrustum(-ar, ar, -1, 1, 3, 25);
99    glMatrixMode(GL_MODELVIEW);
100    glLoadIdentity();
101    glTranslatef(0, 0, -10);
102 
103    /* pass viewport dims to the shader */
104    {
105       GLfloat viewport[4];
106       glGetFloatv(GL_VIEWPORT, viewport);
107       glUniform2f(uViewportSize, viewport[2], viewport[3]);
108    }
109 }
110 
111 
112 static void
MouseMotion(int x,int y)113 MouseMotion(int x, int y)
114 {
115    if (ButtonDown) {
116       float x0 = (2.0 * ButtonX - WinWidth) / WinWidth;
117       float y0 = (WinHeight - 2.0 * ButtonY) / WinHeight;
118       float x1 = (2.0 * x - WinWidth) / WinWidth;
119       float y1 = (WinHeight - 2.0 * y) / WinHeight;
120       float q[4];
121 
122       trackball(q, x0, y0, x1, y1);
123       ButtonX = x;
124       ButtonY = y;
125       add_quats(q, CurQuat, CurQuat);
126 
127       glutPostRedisplay();
128    }
129 }
130 
131 
132 static void
MouseButton(int button,int state,int x,int y)133 MouseButton(int button, int state, int x, int y)
134 {
135   if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
136      ButtonDown = GL_TRUE;
137      ButtonX = x;
138      ButtonY = y;
139   }
140   else if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
141      ButtonDown = GL_FALSE;
142   }
143 }
144 
145 
146 static void
CleanUp(void)147 CleanUp(void)
148 {
149    glDeleteShader(FragShader);
150    glDeleteShader(VertShader);
151    glDeleteShader(GeomShader);
152    glDeleteProgram(Program);
153    glutDestroyWindow(Win);
154 }
155 
156 
157 static void
Key(unsigned char key,int x,int y)158 Key(unsigned char key, int x, int y)
159 {
160   (void) x;
161   (void) y;
162 
163    switch(key) {
164    case ' ':
165    case 'a':
166       Anim = !Anim;
167       if (Anim) {
168          glutIdleFunc(Idle);
169       }
170       else
171          glutIdleFunc(NULL);
172       break;
173    case 27:
174       CleanUp();
175       exit(0);
176       break;
177    }
178    glutPostRedisplay();
179 }
180 
181 
182 static void
Init(void)183 Init(void)
184 {
185    static const char *vertShaderText =
186       "#version 120 \n"
187       "void main() \n"
188       "{ \n"
189       "   gl_FrontColor = gl_Color; \n"
190       "   gl_Position = ftransform(); \n"
191       "} \n";
192    static const char *geomShaderText =
193       "#version 120 \n"
194       "#extension GL_ARB_geometry_shader4: enable \n"
195       "uniform vec2 ViewportSize; \n"
196       "varying vec2 Vert0, Vert1, Vert2; \n"
197       "\n"
198       "// Transform NDC coord to window coord \n"
199       "vec2 vpxform(vec4 p) \n"
200       "{ \n"
201       "   return (p.xy / p.w + 1.0) * 0.5 * ViewportSize; \n"
202       "} \n"
203       "\n"
204       "void main() \n"
205       "{ \n"
206       "   gl_FrontColor = gl_FrontColorIn[0]; \n"
207       "   Vert0 = vpxform(gl_PositionIn[0]); \n"
208       "   Vert1 = vpxform(gl_PositionIn[1]); \n"
209       "   Vert2 = vpxform(gl_PositionIn[2]); \n"
210       "   gl_Position = gl_PositionIn[0]; \n"
211       "   EmitVertex(); \n"
212       "   gl_Position = gl_PositionIn[1]; \n"
213       "   EmitVertex(); \n"
214       "   gl_Position = gl_PositionIn[2]; \n"
215       "   EmitVertex(); \n"
216       "} \n";
217    static const char *fragShaderText =
218       "#version 120 \n"
219       "#define LINE_WIDTH 2.5 \n"
220       "varying vec2 Vert0, Vert1, Vert2; \n"
221       "// Compute distance from a point to a line \n"
222       "float point_line_dist(vec2 p, vec2 v1, vec2 v2) \n"
223       "{ \n"
224       "   float s = (v2.x - v1.x) * (v1.y - p.y) - (v1.x - p.x) * (v2.y - v1.y); \n"
225       "   float t = length(v2 - v1); \n"
226       "   return abs(s) / t; \n"
227       "} \n"
228       "\n"
229       "void main() \n"
230       "{ \n"
231       "   float d0 = point_line_dist(gl_FragCoord.xy, Vert0, Vert1); \n"
232       "   float d1 = point_line_dist(gl_FragCoord.xy, Vert1, Vert2); \n"
233       "   float d2 = point_line_dist(gl_FragCoord.xy, Vert2, Vert0); \n"
234       "   float m = min(d0, min(d1, d2)); \n"
235       "   gl_FragColor = gl_Color * smoothstep(0.0, LINE_WIDTH, m); \n"
236       "} \n";
237 
238    if (!ShadersSupported())
239       exit(1);
240 
241    if (!glutExtensionSupported("GL_ARB_geometry_shader4")) {
242       fprintf(stderr, "Sorry, GL_ARB_geometry_shader4 is not supported.\n");
243       exit(1);
244    }
245 
246    VertShader = CompileShaderText(GL_VERTEX_SHADER, vertShaderText);
247    FragShader = CompileShaderText(GL_FRAGMENT_SHADER, fragShaderText);
248    GeomShader = CompileShaderText(GL_GEOMETRY_SHADER_ARB, geomShaderText);
249 
250    Program = LinkShaders3(VertShader, GeomShader, FragShader);
251    assert(Program);
252    CheckError(__LINE__);
253 
254    /*
255     * The geometry shader will receive and emit triangles.
256     */
257    glProgramParameteriARB(Program, GL_GEOMETRY_INPUT_TYPE_ARB,
258                           GL_TRIANGLES);
259    glProgramParameteriARB(Program, GL_GEOMETRY_OUTPUT_TYPE_ARB,
260                           GL_TRIANGLE_STRIP);
261    glProgramParameteriARB(Program,GL_GEOMETRY_VERTICES_OUT_ARB, 3);
262    CheckError(__LINE__);
263 
264    /* relink */
265    glLinkProgramARB(Program);
266 
267    assert(glIsProgram(Program));
268    assert(glIsShader(FragShader));
269    assert(glIsShader(VertShader));
270    assert(glIsShader(GeomShader));
271 
272    glUseProgram(Program);
273 
274    uViewportSize = glGetUniformLocation(Program, "ViewportSize");
275 
276    glClearColor(0.3f, 0.3f, 0.3f, 0.0f);
277    glEnable(GL_DEPTH_TEST);
278 
279    printf("GL_RENDERER = %s\n",(const char *) glGetString(GL_RENDERER));
280 }
281 
282 
283 int
main(int argc,char * argv[])284 main(int argc, char *argv[])
285 {
286    glutInit(&argc, argv);
287    glutInitWindowSize(WinWidth, WinHeight);
288    glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
289    Win = glutCreateWindow(argv[0]);
290    gladLoadGL();
291    glutReshapeFunc(Reshape);
292    glutKeyboardFunc(Key);
293    glutDisplayFunc(Redisplay);
294    glutMotionFunc(MouseMotion);
295    glutMouseFunc(MouseButton);
296    if (Anim)
297       glutIdleFunc(Idle);
298 
299    Init();
300    glutMainLoop();
301    return 0;
302 }
303