// shaded.java
// Draws a shaded sphere in OpenGL
//
// Copyright (C) 2004-2005, 2009 Frank C. Langbein, http://www.langbein.org/
//
// Conversion to Java by Simon Manasseh <cookie@woaf.net>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
//
// Differences from C source:
// - GLUT not included in jogl, so replaced with AWT toolkit functions
// - Teapot class (c) Mark J. Kilgard 1994, see below for more information
//
// Requires jogl libraries available from http://jogl.dev.java.net/
// Compile as any standard java program, make sure your class and
// library paths include the jogl libraries! 
//
// See the jogl documentation for more information. Note that you have
// to install jogl.jar and jogl-natives-linux.jar (or the corresponding
// natives file for your platform).
//
// JOGL version JSR231b05 is installed in /opt/jogl-JSR231b05 in the
// COMSC Linux labs.
//
// Under Linux add the class and libraries using the following:
//
//   if you are using the bourne shell / bash:
//
//     export CLASSPATH=$CLASSPATH:/PATH/TO/JOGL/CLASSES
//     export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/PATH/TO/JOGL/CLASSES
//
//   if you are using the c-shell / tcsh:
//
//     setenv CLASSPATH {$CLASSPATH}:/PATH/TO/JOGL/CLASSES
//     setenv LD_LIBRARY_PATH {$LD_LIBRARY_PATH}:/PATH/TO/JOGL/CLASSES
//
//     (add the CLASSPATH / LD_LIBRARY_PATH variable in the second argument
//      only if they are defined, otherwise you'll get an error message)
//
// Also make sure . (the current directory) is in your CLASSPATH if you want
// to make it simple for java to find the executable.
//
// Compile with
//
//   javac shaded.java
//
// Run with
//
//   javac shaded
//


// Import AWT libraries
import java.awt.*;
import java.awt.event.*;

// Import jogl library
import com.sun.opengl.util.FPSAnimator;
import javax.media.opengl.GL;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;


// shaded class
public class shaded extends Frame {

  private GLCanvas glCanvas;
  private FPSAnimator animator;
  private Dimension dimension;

  // Setup window
  public shaded(Dimension dim) {

    super("CM0304 - Graphics - shaded scene");

    dimension = dim;
    super.setSize(dim);

    glCanvas = new GLCanvas (new GLCapabilities ());
    glCanvas.addGLEventListener(new Renderer());

    add(glCanvas, java.awt.BorderLayout.CENTER);
    animator = new FPSAnimator (glCanvas, 60);
  }

  // Method to return glCanvas
  public GLCanvas getGLCanvas() {
    return glCanvas;
  }

  // Method to return animator
  public FPSAnimator getAnimator() {
    return animator;
  }

  // Rendering methods, etc. for the scene
  static class Renderer implements GLEventListener, KeyListener,
                                   MouseListener, MouseMotionListener {

    ///////////////////////////////////////////////////////////////////////
    // Constants, global variables, etc.

    // Shortcuts for math constants and functions
    final double PI = Math.PI;
    final double PI_2 = PI / 2;
    final double sin(double i) {
      return Math.sin(i); 
    }
    final double cos(double i) { 
      return Math.cos(i); 
    }

    // Conversion factors for degrees to radians
    final double deg_to_rad = PI / 180;
    final double rad_to_deg = 180 / PI;

    // Id of display list containing the scene describtion
    private int scene_dl;

    // Track mouse position
    int mouse_x = 0;
    int mouse_y = 0;

    // Camera position
    double camera_x = 3.0;
    double camera_y = 0.0;
    double camera_z = 0.0;
    double camera_yaw = 0.0;
    double camera_pitch = 0.0;

    // Camera sensitivity
    final double angle_sensitivity = 0.005;
    final double dist_sensitivity = 0.01;

    // Scene rotation
    double scene_yaw = 0.0;
    double scene_pitch = 0.0;

    // GLU
    private GLU glu = new GLU ();



    //////////////////////////////////////////////////////////////////////
    // Methods to draw the models in the scene

    // Draw a sphere
    private void sphere(GL gl) {

      // Angle steps for facet size
      double da = 5.0;

      // Sphere material
      float diffuse[] = {0.7f, 0.0f, 0.0f, 1.0f};
      float ambient[] = {0.5f, 0.0f, 0.0f, 1.0f};
      float specular[] = {1.0f, 1.0f, 1.0f, 1.0f};
      float shine[] = {127.0f};

      // Set material
      gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT, ambient, 0);
      gl.glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE, diffuse, 0);
      gl.glMaterialfv(GL.GL_FRONT, GL.GL_SPECULAR, specular, 0);
      gl.glMaterialfv(GL.GL_FRONT, GL.GL_SHININESS, shine, 0);

      // For shading we need surface normals in addition to vertex
      // positions; for the unit sphere the normal is the same than the
      // surface position (note that this is a very special case!).
      //
      // We first set the normal of the vertex and then the
      // vertex itself.

      // Quadrilateral strips
      for (double phi = -90.0 + da; phi < 90.0; phi += da) {
        gl.glBegin (GL.GL_QUAD_STRIP);
          for (double thet = -180.0; thet <= 180.0; thet += da) {
            gl.glNormal3d (sin (deg_to_rad * thet) * cos (deg_to_rad * phi),
                           cos (deg_to_rad * thet) * cos (deg_to_rad * phi),
                           sin (deg_to_rad * phi));
            gl.glVertex3d (sin (deg_to_rad * thet) * cos (deg_to_rad * phi),
                           cos (deg_to_rad * thet) * cos (deg_to_rad * phi),
                           sin (deg_to_rad * phi));
            gl.glNormal3d (sin (deg_to_rad * thet) * cos (deg_to_rad * (phi + da)),
                           cos (deg_to_rad * thet) * cos (deg_to_rad * (phi + da)),
                           sin (deg_to_rad * (phi + da)));
            gl.glVertex3d (sin (deg_to_rad * thet) * cos (deg_to_rad * (phi + da)),
                           cos (deg_to_rad * thet) * cos (deg_to_rad * (phi + da)),
                           sin (deg_to_rad * (phi + da)));
        }
        gl.glEnd();
      }

      // North pole
      gl.glBegin (GL.GL_TRIANGLE_FAN);
      gl.glNormal3d (0.0, 0.0, 1.0);
      gl.glVertex3d (0.0, 0.0, 1.0);
      for (double thet = -180.0; thet <= 180.0; thet += 10.0) {
        gl.glNormal3d (sin (deg_to_rad * thet) * cos (deg_to_rad * (90.0 - da)),
                       cos (deg_to_rad * thet) * cos (deg_to_rad * (90.0 - da)),
                       sin (deg_to_rad * 80.0));
        gl.glVertex3d (sin (deg_to_rad * thet) * cos (deg_to_rad * (90.0 - da)),
                       cos (deg_to_rad * thet) * cos (deg_to_rad * (90.0 - da)),
                       sin (deg_to_rad * 80.0));
      }
      gl.glEnd();

      // South pole
      gl.glBegin (GL.GL_TRIANGLE_FAN);
      gl.glNormal3d (0.0, 0.0, -1.0);
      gl.glVertex3d (0.0, 0.0, -1.0);
      for (double thet = -180.0; thet <= 180.0; thet += 10.0) {
        gl.glNormal3d (sin (deg_to_rad * thet) * cos (deg_to_rad * (-90.0 + da)),
                       cos (deg_to_rad * thet) * cos (deg_to_rad * (-90.0 + da)),
                       sin (deg_to_rad * (-90.0 + da)));
        gl.glVertex3d (sin (deg_to_rad * thet) * cos (deg_to_rad * (-90.0 + da)),
                       cos (deg_to_rad * thet) * cos (deg_to_rad * (-90.0 + da)),
                       sin (deg_to_rad * (-90.0 + da)));
      }
      gl.glEnd();
    }

    // Draw a teapot
    private void teapot(GL gl) {
      // Teapot material
      float diffuse[] = {0.0f, 0.7f, 0.0f, 1.0f};
      float ambient[] = {0.0f, 0.5f, 0.0f, 1.0f};
      float specular[] = {1.0f, 1.0f, 1.0f, 1.0f};
      float shine[] = {127.0f};

      // Set material
      gl.glMaterialfv (GL.GL_FRONT, GL.GL_AMBIENT, ambient, 0);
      gl.glMaterialfv (GL.GL_FRONT, GL.GL_DIFFUSE, diffuse, 0);
      gl.glMaterialfv (GL.GL_FRONT, GL.GL_SPECULAR, specular, 0);
      gl.glMaterialfv (GL.GL_FRONT, GL.GL_SHININESS, shine, 0);

      // Push current modelview matrix on a matrix stack to save current
      // transformation.
      gl.glPushMatrix ();

      // Multiply modelview matrix with additional transformation to move
      // and rotate the teapot to a different position.
      gl.glTranslated (0.0, 3.0, 0.0);
      gl.glRotated (90.0, 1.0, 0.0, 0.0);

      // Glut function for drawing a solid teapot
      Teapot.glutSolidTeapot (gl, 1.0);

      // Get original matrix back from stack (undo above transformation
      // for objects drawn after this one)
      gl.glPopMatrix ();
    }

    // Main method called to setup the scene contents
    private void scene(GL gl) {
      // Draw teapot
      teapot(gl);

      // Draw sphere
      sphere(gl);
    }

    //////////////////////////////////////////////////////////////////////
    // Lights

    // Initialise lighting
    private void init_lights(GL gl) {
      // Specify light emitted by light source 0
      float diffuse[] = {1.0f, 1.0f, 1.0f, 1.0f};
      float ambient[] = {0.3f, 0.3f, 0.3f, 1.0f};
      float specular[] = {1.0f, 1.0f, 1.0f, 1.0f};
      float attenuation[] = {1.0f};

      // Enable lighting
      gl.glEnable (GL.GL_LIGHTING);

      // Enable first light source
      gl.glEnable (GL.GL_LIGHT0);
      // Set light emitted by light source 0
      gl.glLightfv (GL.GL_LIGHT0, GL.GL_AMBIENT, ambient, 0);
      gl.glLightfv (GL.GL_LIGHT0, GL.GL_DIFFUSE, diffuse, 0);
      gl.glLightfv (GL.GL_LIGHT0, GL.GL_SPECULAR, specular, 0);
      gl.glLightfv (GL.GL_LIGHT0, GL.GL_CONSTANT_ATTENUATION, attenuation, 0);
    }

    // Render (set position of) light source
    private void lights(GL gl) {
      // Light source 0 position
      float light0_pos[] = {10f, 15f, 20f, 1.0f};

      // Set light source 0 position
      gl.glLightfv (GL.GL_LIGHT0, GL.GL_POSITION, light0_pos, 0);
    }

    //////////////////////////////////////////////////////////////////////
    // OpenGL functions

    // Initialise OpenGL
    public void init(GLAutoDrawable glDrawable) {
      final GL gl = glDrawable.getGL();

      glDrawable.addKeyListener(this);
      glDrawable.addMouseListener(this);
      glDrawable.addMouseMotionListener(this);

      // Set clear color to black
      gl.glClearColor (0.0f, 0.0f, 0.0f, 1.0f);

      // Enable visible surface detection via depth tests
      gl.glDepthFunc (GL.GL_LEQUAL);
      gl.glClearDepth (1.0f);
      gl.glEnable (GL.GL_DEPTH_TEST);

      // Fill polygons for shading
      gl.glPolygonMode (GL.GL_FRONT, GL.GL_FILL);

      // Treat polygon front and back side equal (no two-sided polygons)
      gl.glLightModeli (GL.GL_LIGHT_MODEL_TWO_SIDE, GL.GL_FALSE);

      // Enable local viewer model for specular light
      gl.glLightModeli (GL.GL_LIGHT_MODEL_LOCAL_VIEWER, GL.GL_TRUE);

      // Enable smoooth shading (multi-coloured polygons)
      gl.glShadeModel (GL.GL_SMOOTH);

      // Initialise lights
      init_lights (gl);

      // Store scene in a display list: 
      //   first create one display list
      scene_dl = gl.glGenLists(1);
      //   and then call the rendering functions to store the primitives
      //   in the list
      gl.glNewList(scene_dl, GL.GL_COMPILE);
      scene (gl);
      gl.glEndList();
    }

    // Display event: render the scene
    public void display(GLAutoDrawable glDrawable) {
      final GL gl = glDrawable.getGL();

      // Clear frame buffer
      gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

      // Setup modelview matrix (model and viewing transformation)
      gl.glMatrixMode (GL.GL_MODELVIEW);
      gl.glLoadIdentity ();

      // Camera
      glu.gluLookAt (camera_x + cos (camera_yaw) * cos (camera_pitch),
                     camera_y + sin (camera_yaw) * cos (camera_pitch),
                     camera_z + sin (camera_pitch),
                     camera_x, camera_y, camera_z,
                     0.0, 0.0, 1.0);

      // Scene
      gl.glRotated (scene_yaw * rad_to_deg, 0.0, 0.0, 1.0);
      gl.glRotated (scene_pitch * rad_to_deg, 0.0, 1.0, 0.0);

      // Set light positions: fixed position with respect to objects; if
      // fixed position with respect to viewer (e.g. viewer has a flash
      // light) draw after setting modelview matrix to identity above.
      lights (gl);

      // Render scene by calling display list
      // (without display lists we'd call scene () here...)
      gl.glCallList (scene_dl);

      // Flush OpenGL pipeline
      gl.glFlush ();
    }

    // the displayChanged method isn't actually implemented in jogl.... 
    // yet...
    public void displayChanged(GLAutoDrawable glDrawable, boolean modeChanged,
                                boolean deviceChanged) {
    }

    // Window reshape event
    public void reshape(GLAutoDrawable glDrawable, int x, int y, int w,
                         int h) {
      final GL gl = glDrawable.getGL();

      // Setup viewport size
      gl.glViewport(0, 0, w, h);

      // Setup projection matrix
      gl.glMatrixMode(GL.GL_PROJECTION);
      gl.glLoadIdentity();

      // Perspective projection
      glu.gluPerspective(60.0, (double)w / (double)h, 0.1, 1000.0);
    }

    //////////////////////////////////////////////////////////////////////
    // Keyboard events

    public void keyPressed(KeyEvent e) {
      if (e.getKeyCode() == KeyEvent.VK_ESCAPE)
        System.exit(0);
    }

    public void keyReleased(KeyEvent e) {
    }

    public void keyTyped(KeyEvent e) {
    }

    //////////////////////////////////////////////////////////////////////
    // Mouse events

    // Do-nothing methods, but required nonetheless
    public void mouseEntered(MouseEvent e) {
    }

    public void mouseExited(MouseEvent e) {
    }

    public void mousePressed(MouseEvent e) {
    }

    public void mouseReleased(MouseEvent e) {
    }

    public void mouseClicked(MouseEvent e) {
    }

    // Mouse event called when mouse button is pressed and moved
    public void mouseDragged(MouseEvent e) {

      int x = e.getX();
      int y = e.getY();

      // Button 1 pressed: change viewing direction
      if ((e.getModifiers() & e.BUTTON1_MASK) != 0) {
        camera_yaw += angle_sensitivity * (double) (mouse_x - x);
        if (camera_yaw > PI) {
          camera_yaw -= 2.0 * PI;
        } else if (camera_yaw < -PI) {
          camera_yaw += 2.0 * PI;
        }
        camera_pitch += angle_sensitivity * (double) (y - mouse_y);
        if (camera_pitch > PI_2) {
          camera_pitch = PI_2;
        } else if (camera_pitch < -PI_2) {
          camera_pitch = -PI_2;
        }
      }

      // Button 2 pressed: change position
      if ((e.getModifiers() & e.BUTTON2_MASK) != 0) {
        camera_x += dist_sensitivity * (double) (y - mouse_y)
                    * cos (camera_yaw) * cos (camera_pitch);
        camera_y += dist_sensitivity * (double) (y - mouse_y)
                    * sin (camera_yaw) * cos (camera_pitch);
        camera_z += dist_sensitivity * (double) (y - mouse_y)
                    * sin (camera_pitch);
      }

      // Button 3 pressed: rotate scene
      if ((e.getModifiers() & e.BUTTON3_MASK) != 0) {
        scene_yaw += angle_sensitivity * (double) (x - mouse_x);
        if (scene_yaw > PI) {
          scene_yaw -= 2.0 * PI;
        } else if (scene_yaw < -PI) {
          scene_yaw += 2.0 * PI;
        }
        scene_pitch += angle_sensitivity * (double) (y - mouse_y);
        if (scene_pitch > PI) {
          scene_pitch -= 2.0 * PI;
        } else if (scene_pitch < -PI) {
          scene_pitch += 2.0 * PI;
        }
      }

      mouse_x = x;
      mouse_y = y;
    }

    // Passive motion callback to capture mouse movements while buttons
    // are not pressed
    public void mouseMoved(MouseEvent e) {
      // Update mouse position
      mouse_x = e.getX();
      mouse_y = e.getY();
    }

  }
  // end of renderer class


  //////////////////////////////////////////////////////////////////////////
  // MAIN

  public static void main(String[] args) {
    final shaded scene = new shaded(new Dimension(500,500));
    scene.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        scene.getAnimator().stop();
        System.exit(0);
      }
    });

    scene.setVisible(true);
    scene.getAnimator().start();
    scene.getGLCanvas().requestFocus();
  }

}


//////////////////////////////////////////////////////////////////////////////
// Below is a port of the GLUT function to draw the teapot. It is included
// here as GLUT is not available with jogl. 


/** Draw the famous teapot 
    Copyright (c) Mark J. Kilgard, 1994. 
*/

/**
(c) Copyright 1993, Silicon Graphics, Inc.
   
ALL RIGHTS RESERVED
   
Permission to use, copy, modify, and distribute this software
for any purpose and without fee is hereby granted, provided
that the above copyright notice appear in all copies and that
both the copyright notice and this permission notice appear in
supporting documentation, and that the name of Silicon
Graphics, Inc. not be used in advertising or publicity
pertaining to distribution of the software without specific,
written prior permission.

THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU
"AS-IS" AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR
OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  IN NO
EVENT SHALL SILICON GRAPHICS, INC.  BE LIABLE TO YOU OR ANYONE
ELSE FOR ANY DIRECT, SPECIAL, INCIDENTAL, INDIRECT OR
CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER,
INCLUDING WITHOUT LIMITATION, LOSS OF PROFIT, LOSS OF USE,
SAVINGS OR REVENUE, OR THE CLAIMS OF THIRD PARTIES, WHETHER OR
NOT SILICON GRAPHICS, INC.  HAS BEEN ADVISED OF THE POSSIBILITY
OF SUCH LOSS, HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
ARISING OUT OF OR IN CONNECTION WITH THE POSSESSION, USE OR
PERFORMANCE OF THIS SOFTWARE.

US Government Users Restricted Rights

Use, duplication, or disclosure by the Government is subject to
restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
(c)(1)(ii) of the Rights in Technical Data and Computer
Software clause at DFARS 252.227-7013 and/or in similar or
successor clauses in the FAR or the DOD or NASA FAR
Supplement.  Unpublished-- rights reserved under the copyright
laws of the United States.  Contractor/manufacturer is Silicon
Graphics, Inc., 2011 N.  Shoreline Blvd., Mountain View, CA
94039-7311.

OpenGL(TM) is a trademark of Silicon Graphics, Inc.
*/

class Teapot {

  /* Rim, body, lid, and bottom data must be reflected in x and
     y; handle and spout data across the y axis only.  */

  static int patchdata[][] =
  {
    /* rim */
    {102, 103, 104, 105, 4, 5, 6, 7, 8, 9, 10, 11,
     12, 13, 14, 15},
    /* body */
    {12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
     24, 25, 26, 27},
    {24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36,
     37, 38, 39, 40},
    /* lid */
    {96, 96, 96, 96, 97, 98, 99, 100, 101, 101, 101,
     101, 0, 1, 2, 3,},
    {0, 1, 2, 3, 106, 107, 108, 109, 110, 111, 112,
     113, 114, 115, 116, 117},
    /* bottom */
    {118, 118, 118, 118, 124, 122, 119, 121, 123, 126,
     125, 120, 40, 39, 38, 37},
    /* handle */
    {41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
     53, 54, 55, 56},
    {53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
     28, 65, 66, 67},
    /* spout */
    {68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
     80, 81, 82, 83},
    {80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
     92, 93, 94, 95}
  };

  static float cpdata[][] = {
    {0.2f, 0f, 2.7f}, {0.2f, -0.112f, 2.7f}, {0.112f, -0.2f, 2.7f}, 
    {0f,-0.2f, 2.7f}, {1.3375f, 0f, 2.53125f}, 
    {1.3375f, -0.749f, 2.53125f}, {0.749f, -1.3375f, 2.53125f}, 
    {0f, -1.3375f, 2.53125f}, {1.4375f, 0f, 2.53125f}, 
    {1.4375f, -0.805f, 2.53125f}, {0.805f, -1.4375f, 2.53125f},
    {0f, -1.4375f, 2.53125f}, {1.5f, 0f, 2.4f}, {1.5f, -0.84f, 2.4f}, 
    {0.84f, -1.5f, 2.4f}, {0f, -1.5f, 2.4f}, {1.75f, 0f, 1.875f},
    {1.75f, -0.98f, 1.875f}, {0.98f, -1.75f, 1.875f}, {0f, -1.75f, 1.875f},
    {2f, 0f, 1.35f}, {2f, -1.12f, 1.35f}, {1.12f, -2f, 1.35f},
    {0f, -2f, 1.35f}, {2f, 0f, 0.9f}, {2f, -1.12f, 0.9f},
    {1.12f, -2f, 0.9f}, {0f, -2f, 0.9f}, {-2f, 0f, 0.9f}, {2f, 0f, 0.45f},
    {2f, -1.12f, 0.45f}, {1.12f, -2f, 0.45f}, {0f, -2f, 0.45f},
    {1.5f, 0f, 0.225f}, {1.5f, -0.84f, 0.225f}, {0.84f, -1.5f, 0.225f},
    {0f, -1.5f, 0.225f}, {1.5f, 0f, 0.15f}, {1.5f, -0.84f, 0.15f},
    {0.84f, -1.5f, 0.15f}, {0f, -1.5f, 0.15f}, {-1.6f, 0f, 2.025f},
    {-1.6f, -0.3f, 2.025f}, {-1.5f, -0.3f, 2.25f}, {-1.5f, 0f, 2.25f},
    {-2.3f, 0f, 2.025f}, {-2.3f, -0.3f, 2.025f}, {-2.5f, -0.3f, 2.25f},
    {-2.5f, 0f, 2.25f}, {-2.7f, 0f, 2.025f}, {-2.7f, -0.3f, 2.025f},
    {-3f, -0.3f, 2.25f}, {-3f, 0f, 2.25f}, {-2.7f, 0f, 1.8f},
    {-2.7f, -0.3f, 1.8f}, {-3f, -0.3f, 1.8f}, {-3f, 0f, 1.8f},
    {-2.7f, 0f, 1.575f}, {-2.7f, -0.3f, 1.575f}, {-3f, -0.3f, 1.35f},
    {-3f, 0f, 1.35f}, {-2.5f, 0f, 1.125f}, {-2.5f, -0.3f, 1.125f},
    {-2.65f, -0.3f, 0.9375f}, {-2.65f, 0f, 0.9375f}, {-2f, -0.3f, 0.9f},
    {-1.9f, -0.3f, 0.6f}, {-1.9f, 0f, 0.6f}, {1.7f, 0f, 1.425f},
    {1.7f, -0.66f, 1.425f}, {1.7f, -0.66f, 0.6f}, {1.7f, 0f, 0.6f},
    {2.6f, 0f, 1.425f}, {2.6f, -0.66f, 1.425f}, {3.1f, -0.66f, 0.825f},
    {3.1f, 0f, 0.825f}, {2.3f, 0f, 2.1f}, {2.3f, -0.25f, 2.1f},
    {2.4f, -0.25f, 2.025f}, {2.4f, 0f, 2.025f}, {2.7f, 0f, 2.4f},
    {2.7f, -0.25f, 2.4f}, {3.3f, -0.25f, 2.4f}, {3.3f, 0f, 2.4f},
    {2.8f, 0f, 2.475f}, {2.8f, -0.25f, 2.475f}, {3.525f, -0.25f, 2.49375f},
    {3.525f, 0f, 2.49375f}, {2.9f, 0f, 2.475f}, {2.9f, -0.15f, 2.475f},
    {3.45f, -0.15f, 2.5125f}, {3.45f, 0f, 2.5125f}, {2.8f, 0f, 2.4f},
    {2.8f, -0.15f, 2.4f}, {3.2f, -0.15f, 2.4f}, {3.2f, 0f, 2.4f},
    {0f, 0f, 3.15f}, {0.8f, 0f, 3.15f}, {0.8f, -0.45f, 3.15f},
    {0.45f, -0.8f, 3.15f}, {0f, -0.8f, 3.15f}, {0f, 0f, 2.85f},
    {1.4f, 0f, 2.4f}, {1.4f, -0.784f, 2.4f}, {0.784f, -1.4f, 2.4f},
    {0f, -1.4f, 2.4f}, {0.4f, 0f, 2.55f}, {0.4f, -0.224f, 2.55f},
    {0.224f, -0.4f, 2.55f}, {0f, -0.4f, 2.55f}, {1.3f, 0f, 2.55f},
    {1.3f, -0.728f, 2.55f}, {0.728f, -1.3f, 2.55f}, {0f, -1.3f, 2.55f},
    {1.3f, 0f, 2.4f}, {1.3f, -0.728f, 2.4f}, {0.728f, -1.3f, 2.4f},
    {0f, -1.3f, 2.4f}, {0f, 0f, 0f}, {1.425f, -0.798f, 0f},
    {1.5f, 0f, 0.075f}, {1.425f, 0f, 0f}, {0.798f, -1.425f, 0f},
    {0f, -1.5f, 0.075f}, {0f, -1.425f, 0f}, {1.5f, -0.84f, 0.075f},
    {0.84f, -1.5f, 0.075f}
  };

  static float tex[] = {
    0f, 0f,
    1f, 0f,
    0f, 1f,
    1f, 1f
  };

  static void teapot(GL gl, int grid, float scale, int type) {
    float[] p = new float [48]; //4*4*3
    float[] q = new float [48];
    float[] r = new float [48];
    float[] s = new float [48];
    int i, j, k, l;

    gl.glPushAttrib(GL.GL_ENABLE_BIT | GL.GL_EVAL_BIT);
    gl.glEnable(GL.GL_AUTO_NORMAL);
    gl.glEnable(GL.GL_NORMALIZE);
    gl.glEnable(GL.GL_MAP2_VERTEX_3);
    gl.glEnable(GL.GL_MAP2_TEXTURE_COORD_2);
    gl.glPushMatrix();
    gl.glRotatef(270.0f, 1.0f, 0.0f, 0.0f);
    gl.glScalef(0.5f * scale, 0.5f * scale, 0.5f * scale);
    gl.glTranslatef(0.0f, 0.0f, -1.5f);
    for (i = 0; i < 10; i++) {
      for (j = 0; j < 4; j++) {
        for (k = 0; k < 4; k++) {
          for (l = 0; l < 3; l++) {
            p[12*j+3*k+l] = cpdata[patchdata[i][j * 4 + k]][l];
            q[12*j+3*k+l] = cpdata[patchdata[i][j * 4 + (3 - k)]][l];
            if (l == 1)
              q[12*j+3*k+l] *= -1.0;
            if (i < 6) {
              r[12*j+3*k+l] = cpdata[patchdata[i][j * 4 + (3 - k)]][l];
              if (l == 0)
                r[12*j+3*k+l] *= -1.0;
              s[12*j+3*k+l] = cpdata[patchdata[i][j * 4 + k]][l];
              if (l == 0)
                s[12*j+3*k+l] *= -1.0;
              if (l == 1)
                s[12*j+3*k+l] *= -1.0;
            }
          }
        }
      }
      gl.glMap2f(GL.GL_MAP2_TEXTURE_COORD_2, 0f, 1f, 2, 2, 0f, 1f, 4, 2, tex, 0);
      gl.glMap2f(GL.GL_MAP2_VERTEX_3, 0f, 1f, 3, 4, 0f, 1f, 12, 4, p, 0);
      gl.glMapGrid2f(grid, 0.0f, 1.0f, grid, 0.0f, 1.0f);
      gl.glEvalMesh2(type, 0, grid, 0, grid);
      gl.glMap2f(GL.GL_MAP2_VERTEX_3, 0f, 1f, 3, 4, 0f, 1f, 12, 4, q, 0);
      gl.glEvalMesh2(type, 0, grid, 0, grid);
      if (i < 6) {
        gl.glMap2f(GL.GL_MAP2_VERTEX_3, 0f, 1f, 3, 4, 0f, 1f, 12, 4, r, 0);
        gl.glEvalMesh2(type, 0, grid, 0, grid);
        gl.glMap2f(GL.GL_MAP2_VERTEX_3, 0f, 1f, 3, 4, 0f, 1f, 12, 4, s, 0);
        gl.glEvalMesh2(type, 0, grid, 0, grid);
      }
    }
    gl.glPopMatrix();
    gl.glPopAttrib();
  }

  static void glutSolidTeapot(GL gl, double scale) {
    teapot(gl, 14, (float)scale, GL.GL_FILL);
  }

  static void glutWireTeapot(GL gl, double scale) {
    teapot(gl, 10, (float)scale, GL.GL_LINE);
  }

}

