JMU
Scientific Visualization and Animation using Bezier Surfaces
An Introduction


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu


Mathematical Representation
Bezier Surfaces in OpenGL
Bezier Surfaces in OpenGL (cont.)
Using an Evaluator
svaexamples/surfaces/edit.c (Fragment: mesh)
           //
   // The evaluator
   glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, uSize, 0, 1, uSize*3, vSize, 
           &points[0][0][0]);

   // Draw the mesh
   glEvalMesh2(GL_LINE, 0, gridSize, 0, gridSize);

        
Bezier Surfaces in OpenGL (cont.)
Initialization
svaexamples/surfaces/edit.c (Fragment: setup)
        
   // Enable the 2-dimensional evaluator
   glEnable(GL_MAP2_VERTEX_3);
   glMapGrid2f(gridSize, 0.0, 1.0, gridSize, 0.0, 1.0);

        
A Simple Surface Editing Program
svaexamples/surfaces/edit.c
        #include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>

#include "../sva/sva.h"  // Must be after windows.h
#include "GL/gl.h"


/**
 * An application for "editing" Bezier surfaces
 *
 * Mouse Commands:
 *
 *   Left-Drag:  To move a point
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */

/**
 * A second order surface
 */
GLfloat points[2][2][3] =
{
  {
    {-2.0, -2.0,  0.0},
    { 2.0, -2.0,  0.0}},
  {
    {-2.0,  2.0,  0.0},
    { 2.0,  2.0,  0.0}}
};
int        uSize         = 2;
int        vSize         = 2;


/*
 * A fourth order surface

GLfloat points[4][4][3] =
{
  {
    {-2.0, -2.0,  0.0},
    {-0.5, -2.0,  0.0},
    { 0.5, -2.0,  0.0},
    { 2.0, -2.0,  0.0}},
  {
    {-2.0, -0.5,  0.0},
    {-0.5, -0.5,  0.0},
    { 0.5, -0.5,  0.0},
    { 2.0, -0.5,  0.0}},
  {
    {-2.0,  0.5,  0.0},
    {-0.5,  0.5,  0.0},
    { 0.5,  0.5,  0.0},
    { 2.0,  0.5,  0.0}},
  {
    {-2.0,  2.0,  0.0},
    {-0.5,  2.0,  0.0},
    { 0.5,  2.0,  0.0},
    { 2.0,  2.0,  0.0}}
};
int        uSize         = 4;
int        vSize         = 4;
*/


// The number of subdivisions in the grid
int        gridSize      = 20;
int        uSelected     = -1;
int        vSelected     = -1;


// Empty SVA functions
void onMouseMove(GLfloat x, GLfloat y){}
void onTimer(int time){}


// Prototypes
GLfloat distanceSquared(GLfloat x1, GLfloat y1, GLfloat z1, 
                        GLfloat x2, GLfloat y2, GLfloat z2);









/**
 * Find the closest point controlling the curve
 *
 * @param x   The x-coordinate to compare to
 * @param y   The y-coordinate to compare to
 * @param z   The z-coordinate to compare to
 * @param ui  The u-index of the clossest point (OUT)
 * @param vi  The v-index of the clossest point (OUR)
 */
void closestPointTo(GLfloat x, GLfloat y, GLfloat z, int &ui, int &vi)
{
   GLfloat d, min;   
   
   ui = -1;
   vi = -1;
   

   min   =  distanceSquared(points[0][0][0], points[0][0][1], points[0][0][2], 
                            x, y, z);
   ui = 0;
   vi = 0;      
   
   for (int u=0; u<uSize; u++)
   {
      for (int v=0; v<vSize; v++)
      {
         
         d = distanceSquared(points[u][v][0], points[u][v][1], points[u][v][2],
                             x, y, z);
         
         if (d < min)
         {
            ui = u;
            vi = v;            
            min   = d;         
         }
      }
   }
}




/**
 * Return the squared Euclidean sistance between two points
 */
GLfloat distanceSquared(GLfloat x1, GLfloat y1, GLfloat z1, 
                        GLfloat x2, GLfloat y2, GLfloat z2)
{
   GLfloat   d;
   
   d = (x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2) + (z1 - z2)*(z1 - z2);

   return d;   
}


/**
 * Display Callback
 */
void onDisplay()
{
   // Clear the screen
   glClear(GL_COLOR_BUFFER_BIT);


   // Set the color
   glColor3f(1.0, 0.0, 0.0);

   //
   // The evaluator
   glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, uSize, 0, 1, uSize*3, vSize, 
           &points[0][0][0]);

   // Draw the mesh
   glEvalMesh2(GL_LINE, 0, gridSize, 0, gridSize);

   

       
    // Show the points
   glPointSize(3.);
   glBegin(GL_POINTS);
   {
      for (int u=0; u<uSize; u++)
      {
         for (int v=0; v<vSize; v++)
         {
            if ((u == uSelected) && (v == vSelected))
            {
               glColor3f(1.0, 0.0, 1.0);
               glVertex3fv(&points[u][v][0]);
            }
            else
            {
               glColor3f(0.0, 1.0, 0.0);
               glVertex3fv(&points[u][v][0]);
            }
         }
      }
      glEnd();
   }
}


/**
 * The mouse callback (i.e., the function that is called
 * each time a mouse button is pressed or released).
 *
 * @param button The button (e.g., GLUT_LEFT_BUTTON)
 * @param state  The state (e.g., GLUT_UP or GLUT_DOWN)
 * @param x      The x-position of the mouse
 * @param y      The y-position of the mouse
 */
void onMouseClick(int button, int state, GLfloat x, GLfloat y)
{
   if(button == SVA_LEFT_BUTTON)
   {
      if (state == SVA_DOWN)
      {
         closestPointTo(x, y, 0.0, uSelected, vSelected);         
      }
      else
      {
         uSelected = -1;         
         vSelected = -1;         
      }

      svaRedisplay();         
   }
}



/**
 * The mouse drag callback
 *
 * @param x      The x-position of the mouse
 * @param y      The y-position of the mouse
 */
void onMouseDrag(GLfloat x, GLfloat y)
{
   if ((uSelected >= 0) && (vSelected >= 0))
   {
      points[uSelected][vSelected][0] = x;
      points[uSelected][vSelected][1] = y;

      svaRedisplay();         
   }
}






/**
 * The entry point of the application.
 *
 * @param argc  The number of command line arguments
 * @param argv  The array of command line arguments
 * @return      A status code
 */
int main(int argc, char **argv)
{
   float min[] = {-4.0, -4.0, -4.0};
   float max[] = { 4.0,  4.0,  4.0};
   int   k;   
   

   
   // Create and initialize the SVA window
   svaCreate(argc, argv, min, max);
   svaShowAxes(false);
   svaSetRotation(0.0, 0.0, 0.0);


   // Enable the 2-dimensional evaluator
   glEnable(GL_MAP2_VERTEX_3);
   glMapGrid2f(gridSize, 0.0, 1.0, gridSize, 0.0, 1.0);


   
   // Start the SVA visualization
   svaStartVisualization();
}
        
Adding Lighting and Shading
Using the Evaluator to Fill
svaexamples/surfaces/shade.c (Fragment: surface)
           //
   // The evaluator
   glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, uSize, 0, 1, uSize*3, vSize, 
           &points[0][0][0]);

   // Fill the surface
   glEvalMesh2(GL_FILL, 0, gridSize, 0, gridSize);

        
Adding Lighting and Shading (cont.)
Initialization
svaexamples/surfaces/shade.c (Fragment: setup)
        
   // Enable lighting and shading
   GLfloat ambient[]   = {0.2, 0.2, 0.2, 1.0};
   GLfloat position[]  = {0.0, 0.0, 2.0, 1.0};
   GLfloat diffuse[]   = {0.7, 0.0, 0.1, 1.0};
   GLfloat specular[]  = {1.0, 1.0, 1.0, 1.0};
   GLfloat shininess[] = {50.0};
      
   glEnable(GL_LIGHTING); // Enable lighting
   glEnable(GL_LIGHT0);   // Enable light 0
   
   glLightfv(GL_LIGHT0, GL_AMBIENT,  ambient);
   glLightfv(GL_LIGHT0, GL_POSITION, position);
   
   glMaterialfv(GL_FRONT, GL_DIFFUSE,   diffuse);
   glMaterialfv(GL_FRONT, GL_SPECULAR,  specular);
   glMaterialfv(GL_FRONT, GL_SHININESS, shininess);

   glEnable(GL_DEPTH_TEST);
   glEnable(GL_AUTO_NORMAL);
   glEnable(GL_NORMALIZE);
   
   // Enable the 2-dimensional evaluator
   glEnable(GL_MAP2_VERTEX_3);
   glMapGrid2f(gridSize, 0.0, 1.0, gridSize, 0.0, 1.0);