shapes demo: added flat shading option (fixed-function only)
[freeglut] / progs / demos / shapes / shapes.c
index ef32988..09fb5ef 100644 (file)
@@ -5,16 +5,18 @@
     in OpenGLUT.  It may also be useful to see which
     parameters control what behavior in the OpenGLUT
     objects.
+
     Spinning wireframe and solid-shaded shapes are
     displayed.  Some parameters can be adjusted.
+
    Keys:
       -    <tt>Esc &nbsp;</tt> Quit
       -    <tt>q Q &nbsp;</tt> Quit
       -    <tt>i I &nbsp;</tt> Show info
       -    <tt>p P &nbsp;</tt> Toggle perspective or orthographic projection
       -    <tt>r R &nbsp;</tt> Toggle fixed or animated rotation around model X-axis
+      -    <tt>s S &nbsp;</tt> Toggle toggle fixed function or shader render path
+      -    <tt>n N &nbsp;</tt> Toggle visualization of object's normal vectors
       -    <tt>= + &nbsp;</tt> Increase \a slices
       -    <tt>- _ &nbsp;</tt> Decreate \a slices
       -    <tt>, < &nbsp;</tt> Decreate \a stacks
@@ -32,7 +34,7 @@
 
     \author  Portions Copyright (C) 2004, the OpenGLUT project contributors. <br>
              OpenGLUT branched from freeglut in February, 2004.
+
     \image   html openglut_shapes.png OpenGLUT Geometric Shapes Demonstration
     \include demos/shapes/shapes.c
 */
@@ -42,6 +44,7 @@
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <stddef.h>
 
 #include "glmatrix.h"
 
@@ -60,7 +63,7 @@ void checkError(const char *functionName)
 }
 
 /*
- * OpenGL 2+ shader mode needs some function and macro definitions, 
+ * OpenGL 2+ shader mode needs some function and macro definitions,
  * avoiding a dependency on additional libraries like GLEW or the
  * GL/glext.h header
  */
@@ -91,11 +94,6 @@ typedef char ourGLchar;
 #define APIENTRY
 #endif
 
-#ifndef GL_VERSION_1_5
-typedef void (APIENTRY *PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers);
-typedef void (APIENTRY *PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer);
-typedef void (APIENTRY *PFNGLBUFFERDATAPROC) (GLenum target, ourGLsizeiptr size, const GLvoid *data, GLenum usage);
-#endif
 #ifndef GL_VERSION_2_0
 typedef GLuint (APIENTRY *PFNGLCREATESHADERPROC) (GLenum type);
 typedef void (APIENTRY *PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const ourGLchar **string, const GLint *length);
@@ -147,6 +145,11 @@ void initExtensionEntries(void)
     gl_GetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC) glutGetProcAddress ("glGetUniformLocation");
     gl_UniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC) glutGetProcAddress ("glUniformMatrix4fv");
     gl_UniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC) glutGetProcAddress ("glUniformMatrix3fv");
+    if (!gl_CreateShader || !gl_ShaderSource || !gl_CompileShader || !gl_CreateProgram || !gl_AttachShader || !gl_LinkProgram || !gl_UseProgram || !gl_GetShaderiv || !gl_GetShaderInfoLog || !gl_GetProgramiv || !gl_GetProgramInfoLog || !gl_GetAttribLocation || !gl_GetUniformLocation || !gl_UniformMatrix4fv || !gl_UniformMatrix3fv)
+    {
+        fprintf (stderr, "glCreateShader, glShaderSource, glCompileShader, glCreateProgram, glAttachShader, glLinkProgram, glUseProgram, glGetShaderiv, glGetShaderInfoLog, glGetProgramiv, glGetProgramInfoLog, glGetAttribLocation, glGetUniformLocation, glUniformMatrix4fv or gl_UniformMatrix3fv not found");
+        exit(1);
+    }
 }
 
 const ourGLchar *vertexShaderSource[] = {
@@ -280,9 +283,10 @@ GLint getAttribOrUniformLocation(const char* name, GLuint program, GLboolean isA
         GLint attrib = gl_GetAttribLocation(program, name);
         if (attrib == -1)
         {
-            fprintf(stderr, "Warning: Could not bind attrib %s\n", name);  
+            fprintf(stderr, "Warning: Could not bind attrib %s\n", name);
         }
 
+        checkError ("getAttribOrUniformLocation");
         return attrib;
     }
     else
@@ -290,16 +294,16 @@ GLint getAttribOrUniformLocation(const char* name, GLuint program, GLboolean isA
         GLint uniform = gl_GetUniformLocation(program, name);
         if (uniform == -1)
         {
-            fprintf(stderr, "Warning: Could not bind uniform %s\n", name);  
+            fprintf(stderr, "Warning: Could not bind uniform %s\n", name);
         }
 
+        checkError ("getAttribOrUniformLocation");
         return uniform;
     }
-    checkError ("getAttribOrUniformLocation");
 }
 
 GLuint program;
-GLint attribute_fg_coord = -1, attribute_fg_normal = -1;  
+GLint attribute_fg_coord = -1, attribute_fg_normal = -1;
 GLint uniform_m = -1, uniform_p = -1, uniform_m_3x3_inv_transp = -1;
 GLint shaderReady = 0;  /* Set to 1 when all initialization went well, to -1 when shader somehow unusable. */
 
@@ -421,6 +425,18 @@ static GLboolean persProject = GL_TRUE;
 static GLboolean animateXRot = GL_FALSE;
 static GLboolean useShader   = GL_FALSE;
 static GLboolean visNormals  = GL_FALSE;
+static GLboolean flat;
+
+/*
+ * Enum to tell drawSizeInfo what to draw for each object
+ */
+#define GEO_NO_SIZE 0
+#define GEO_SIZE 1
+#define GEO_SCALE 2
+#define GEO_INNER_OUTER_RAD 4
+#define GEO_RAD 8
+#define GEO_BASE_HEIGHT 16
+#define GEO_RAD_HEIGHT 32
 
 /*
  * These one-liners draw particular objects, fetching appropriate
@@ -445,28 +461,26 @@ static void drawSolidTorus(void)               { glutSolidTorus(irad,orad,slices
 static void drawWireTorus(void)                { glutWireTorus (irad,orad,slices,stacks);        }
 static void drawSolidSphere(void)              { glutSolidSphere(orad,slices,stacks);            }  /* orad doubles as size input */
 static void drawWireSphere(void)               { glutWireSphere(orad,slices,stacks);             }  /* orad doubles as size input */
-static void drawSolidCone(void)                { glutSolidCone(orad,orad,slices,stacks);         }  /* orad doubles as size input */
-static void drawWireCone(void)                 { glutWireCone(orad,orad,slices,stacks);          }  /* orad doubles as size input */
-static void drawSolidCylinder(void)            { glutSolidCylinder(orad,orad,slices,stacks);     }  /* orad doubles as size input */
-static void drawWireCylinder(void)             { glutWireCylinder(orad,orad,slices,stacks);      }  /* orad doubles as size input */
+static void drawSolidCone(void)                { glutSolidCone(irad,orad,slices,stacks);         }  /* irad doubles as base input, and orad as height input */
+static void drawWireCone(void)                 { glutWireCone(irad,orad,slices,stacks);          }  /* irad doubles as base input, and orad as height input */
+static void drawSolidCylinder(void)            { glutSolidCylinder(irad,orad,slices,stacks);     }  /* irad doubles as radius input, and orad as height input */
+static void drawWireCylinder(void)             { glutWireCylinder(irad,orad,slices,stacks);      }  /* irad doubles as radius input, and orad as height input */
+/* per Glut manpage, it should be noted that the teapot is rendered
+ * with clockwise winding for front facing polygons...
+ * Same for the teacup and teaspoon
+ */
 static void drawSolidTeapot(void)
-{
-    /* per Glut manpage, it should be noted that the teapot is rendered
-     * with clockwise winding for front facing polygons...
-     */
-    glFrontFace(GL_CW);
-    glutSolidTeapot(orad);  /* orad doubles as size input */
-    glFrontFace(GL_CCW);
-}
+{   glFrontFace(GL_CW);    glutSolidTeapot(orad);   glFrontFace(GL_CCW);    /* orad doubles as size input */}
 static void drawWireTeapot(void)
-{
-    /* per Glut manpage, it should be noted that the teapot is rendered
-     * with clockwise winding for front facing polygons...
-     */
-    glFrontFace(GL_CW);
-    glutWireTeapot(orad);  /* orad doubles as size input */
-    glFrontFace(GL_CCW);
-}
+{   glFrontFace(GL_CW);    glutWireTeapot(orad);    glFrontFace(GL_CCW);    /* orad doubles as size input */}
+static void drawSolidTeacup(void)
+{   glFrontFace(GL_CW);    glutSolidTeacup(orad);   glFrontFace(GL_CCW);    /* orad doubles as size input */}
+static void drawWireTeacup(void)
+{   glFrontFace(GL_CW);    glutWireTeacup(orad);    glFrontFace(GL_CCW);    /* orad doubles as size input */}
+static void drawSolidTeaspoon(void)
+{   glFrontFace(GL_CW);    glutSolidTeaspoon(orad); glFrontFace(GL_CCW);    /* orad doubles as size input */}
+static void drawWireTeaspoon(void)
+{   glFrontFace(GL_CW);    glutWireTeaspoon(orad);  glFrontFace(GL_CCW);    /* orad doubles as size input */}
 
 #define RADIUSFAC    0.70710678118654752440084436210485f
 
@@ -527,24 +541,27 @@ typedef struct
     const char * const name;
     void (*solid) (void);
     void (*wire)  (void);
+    int drawSizeInfoFlag;
 } entry;
 
-#define ENTRY(e) {#e, drawSolid##e, drawWire##e}
+#define ENTRY(e,f) {#e, drawSolid##e, drawWire##e,f}
 static const entry table [] =
 {
-    ENTRY (Tetrahedron),
-    ENTRY (Cube),
-    ENTRY (Octahedron),
-    ENTRY (Dodecahedron),
-    ENTRY (RhombicDodecahedron),
-    ENTRY (Icosahedron),
-    ENTRY (SierpinskiSponge),
-    ENTRY (Teapot),
-    ENTRY (Torus),
-    ENTRY (Sphere),
-    ENTRY (Cone),
-    ENTRY (Cylinder),
-    ENTRY (Cuboctahedron)   /* This one doesn't work when in shader mode and is then skipped */
+    ENTRY (Tetrahedron,GEO_NO_SIZE),
+    ENTRY (Cube,GEO_SIZE),
+    ENTRY (Octahedron,GEO_NO_SIZE),
+    ENTRY (Dodecahedron,GEO_NO_SIZE),
+    ENTRY (RhombicDodecahedron,GEO_NO_SIZE),
+    ENTRY (Icosahedron,GEO_NO_SIZE),
+    ENTRY (SierpinskiSponge,GEO_SCALE),
+    ENTRY (Teapot,GEO_SIZE),
+    ENTRY (Teacup,GEO_SIZE),
+    ENTRY (Teaspoon,GEO_SIZE),
+    ENTRY (Torus,GEO_INNER_OUTER_RAD),
+    ENTRY (Sphere,GEO_RAD),
+    ENTRY (Cone,GEO_BASE_HEIGHT),
+    ENTRY (Cylinder,GEO_RAD_HEIGHT),
+    ENTRY (Cuboctahedron,GEO_SIZE)   /* This one doesn't work when in shader mode and is then skipped */
 };
 #undef ENTRY
 
@@ -594,8 +611,66 @@ static void shapesPrintf (int row, int col, const char *fmt, ...)
     glPopMatrix();
 }
 
-/* GLUT callback Handlers */
+/* Print info about the about the current shape and render state on the screen */
+static void DrawSizeInfo(int *row)
+{
+    switch (table [function_index].drawSizeInfoFlag)
+    {
+    case GEO_NO_SIZE:
+        break;
+    case GEO_SIZE:
+        shapesPrintf ((*row)++, 1, "Size  Up  Down : %f", orad);
+        break;
+    case GEO_SCALE:
+        shapesPrintf ((*row)++, 1, "Scale  Up  Down : %f", orad);
+        break;
+    case GEO_INNER_OUTER_RAD:
+        shapesPrintf ((*row)++, 1, "Inner radius Left Right: %f", irad);
+        shapesPrintf ((*row)++, 1, "Outer radius  Up  Down : %f", orad);
+        break;
+    case GEO_RAD:
+        shapesPrintf ((*row)++, 1, "Radius  Up  Down : %f", orad);
+        break;
+    case GEO_BASE_HEIGHT:
+        shapesPrintf ((*row)++, 1, "Base   Left Right: %f", irad);
+        shapesPrintf ((*row)++, 1, "Height  Up  Down : %f", orad);
+        break;
+    case GEO_RAD_HEIGHT:
+        shapesPrintf ((*row)++, 1, "Radius Left Right: %f", irad);
+        shapesPrintf ((*row)++, 1, "Height  Up  Down : %f", orad);
+        break;
+    }
+}
+
+static void drawInfo()
+{
+    int row = 1;
+    shapesPrintf (row++, 1, "Shape PgUp PgDn: %s", table [function_index].name);
+    shapesPrintf (row++, 1, "Slices +-: %d   Stacks <>: %d", slices, stacks);
+    shapesPrintf (row++, 1, "nSides +-: %d   nRings <>: %d", slices, stacks);
+    shapesPrintf (row++, 1, "Depth  (): %d", depth);
+    DrawSizeInfo(&row);
+    if (persProject)
+        shapesPrintf (row++, 1, "Perspective projection (p)");
+    else
+        shapesPrintf (row++, 1, "Orthographic projection (p)");
+    if (useShader) {
+        shapesPrintf (row++, 1, "Using shader (s)");
+    } else {
+        shapesPrintf (row++, 1, "Using fixed function pipeline (s)");
+        if (flat)
+            shapesPrintf (row++, 1, "Flat shading (f)");
+        else
+            shapesPrintf (row++, 1, "Smooth shading (f)");
+    }
+    if (animateXRot)
+        shapesPrintf (row++, 1, "2D rotation (r)");
+    else
+        shapesPrintf (row++, 1, "1D rotation (r)");
+    shapesPrintf (row++, 1, "visualizing normals: %i (n)",visNormals);
+}
 
+/* GLUT callback Handlers */
 static void
 resize(int width, int height)
 {
@@ -612,6 +687,10 @@ static void display(void)
 
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
+    glutSetOption(GLUT_GEOMETRY_VISUALIZE_NORMALS,visNormals);  /* Normals visualized or not? */
+
+    glShadeModel(flat ? GL_FLAT : GL_SMOOTH);  /* flat or gouraud shading */
+
     if (useShader && !shaderReady)
         initShader();
 
@@ -621,6 +700,7 @@ static void display(void)
         gl_UseProgram (program);
         glutSetVertexAttribCoord3(attribute_fg_coord);
         glutSetVertexAttribNormal(attribute_fg_normal);
+        /* There is also a glutSetVertexAttribTexCoord2, which is used only when drawing the teapot, teacup or teaspoon */
 
         gl_matrix_mode(GL_PROJECTION);
         gl_load_identity();
@@ -662,7 +742,6 @@ static void display(void)
     else
     {
         /* fixed function pipeline */
-        glutSetOption(GLUT_OBJECTS_VISUALIZE_NORMALS,visNormals);   /* Normals visualized or not? */
         glMatrixMode(GL_PROJECTION);
         glLoadIdentity();
         if (persProject)
@@ -694,31 +773,12 @@ static void display(void)
         glColor3d(0.1,0.1,0.4);
     }
 
-    if( show_info ) {
-        shapesPrintf (1, 1, "Shape PgUp PgDn: %s", table [function_index].name);
-        shapesPrintf (2, 1, "Slices +-: %d   Stacks <>: %d", slices, stacks);
-        shapesPrintf (3, 1, "nSides +-: %d   nRings <>: %d", slices, stacks);
-        shapesPrintf (4, 1, "Depth  (): %d", depth);
-        shapesPrintf (5, 1, "Outer radius  Up  Down : %f", orad);
-        shapesPrintf (6, 1, "Inner radius Left Right: %f", irad);
-        if (persProject)
-            shapesPrintf (7, 1, "Perspective projection (p)");
-        else
-            shapesPrintf (7, 1, "Orthographic projection (p)");
-        if (useShader)
-            shapesPrintf (8, 1, "Using shader (s)");
-        else
-            shapesPrintf (8, 1, "Using fixed function pipeline (s)");
-        if (animateXRot)
-            shapesPrintf (9, 1, "2D rotation (r)");
-        else
-            shapesPrintf (9, 1, "1D rotation (r)");
-        if (!useShader)
-            shapesPrintf (10, 1, "visualizing normals: %i (n)",visNormals);
-    } else {
+    if( show_info )
+        /* print info to screen */
+        drawInfo();
+    else
         /* print to command line instead */
         printf ( "Shape %d slides %d stacks %d\n", function_index, slices, stacks ) ;
-    }
 
     glutSwapBuffers();
 }
@@ -748,10 +808,10 @@ key(unsigned char key, int x, int y)
     case '.':
     case '>': stacks++;                   break;
 
-    case '9': 
+    case '9':
     case '(': if( depth > -1 ) depth--;   break;
 
-    case '0': 
+    case '0':
     case ')': ++depth;                    break;
 
     case 'P':
@@ -768,6 +828,11 @@ key(unsigned char key, int x, int y)
                 function_index = 0;
         break;
 
+    case 'F':
+    case 'f':
+        flat ^= 1;
+        break;
+
     case 'N':
     case 'n': visNormals=!visNormals;     break;
 
@@ -802,10 +867,12 @@ static void special (int key, int x, int y)
 
     /* Cuboctahedron can't be shown when in shader mode, skip it */
     if (useShader && NUMBEROF (table)-1 == ( unsigned )function_index)
+    {
         if (key==GLUT_KEY_PAGE_UP)
             function_index = 0;
         else
             function_index -= 1;
+    }
 }