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