/*
 * multiview.c
 *
 * Program that shows how to use multiple viewports in a single
 * context 
 *
 * Written by Nate Robins, 1997
 * Modified by Nan Schaller, 2002
 * 	added comments 
 *	replaced rotations with glLookat calls 
 *	removed unnecessary calls
 */

#include <GL/glut.h>

GLfloat spin_x     = 0.0;	// Rotation parameter
GLfloat spin_y     = 0.0;	// Rotation parameter

int old_x, old_y;  		// Mouse coordinates


/*
 * Writes quantrant labels
 */

void text( char* string ) {

    char* p;

    for ( p = string; *p; p++ )
	glutBitmapCharacter( GLUT_BITMAP_HELVETICA_18, *p );

}


/*
 * The world we wish to draw - a GLUT torus 
 */

void drawWorld( void ) {

    glRotatef(spin_y, 1.0, 0.0, 0.0);
    glRotatef(spin_x, 0.0, 1.0, 0.0);
    glutWireTorus( 0.3, 0.5, 16, 32 );

}


/*
 * Initializations
 */
void init( void ) {

    // Use depth (Z) buffering to remove items hidden by others
    glEnable( GL_DEPTH_TEST );
    
}


/*
 * Display callback function; defines what is drawn initially and
 * when glutPostRedisplay() is called
 */
void display( void ) {

    // Get current screen window width and height
    // This actually somewhat eliminates the need for a 
    // reshape callback function.
    int width = glutGet( GLUT_WINDOW_WIDTH );
    int height = glutGet( GLUT_WINDOW_HEIGHT );
    float ratio;
   
    /*
     * DRAW MAIN SCREEN - including divisions and labels
     */
    glViewport( 0, 0, width, height );
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity( );

    gluOrtho2D( 0, width, 0, height );
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity( );

    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    glColor3ub( 255, 255, 255 );
    glBegin( GL_LINES );
       glVertex2i( width / 2, 0 );
       glVertex2i( width / 2, height );
       glVertex2i( 0, height / 2 );
       glVertex2i( width, height / 2 );
    glEnd( );

    // Label quadrants
    glRasterPos2i( 5, 5 );
    text( "Front" );
    glRasterPos2i( width / 2 + 5, 5 );
    text( "Right" );
    glRasterPos2i( 5, height / 2 + 5 );
    text( "Top" );
    glRasterPos2i( width / 2 + 5, height / 2 + 5 );
    text( "Perspective" );

    width = ( width + 1 ) / 2;
    height = ( height + 1 ) / 2;
    ratio = (float) width / height;
    
     
    /*
     * DRAW FRONT VIEW
     */
    glViewport(0, 0, width, height);

    // Specify projection; this actually specifies the view Volume/
    // viewWindow
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity( );
    glOrtho( -ratio, ratio, -ratio, ratio, 1, 256 );

    // Specify viewing/camera/eye coordinate system
    // Observer on Z axis, looking at origin, up is Y axis
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity( );
    gluLookAt( 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 );
    drawWorld();
    

    /*
     * DRAW RIGHT VIEW
     */
    glViewport(width, 0, width, height);

    // Specify projection; this actually specifies the view Volume/
    // viewWindow
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity( );
    glOrtho( -ratio, ratio, -ratio, ratio, 1, 256 );

    // Specify viewing/camera/eye coordinate system
    // Observer on X axis, looking at origin, up is Y axis
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity( );
    gluLookAt( 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 );

    drawWorld();


    /*
     * DRAW TOP VIEW
     */
    glViewport(0, height, width, height);

    // Specify projection; this actually specifies the view Volume/
    // viewWindow
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity( );
    glOrtho( -ratio, ratio, -ratio, ratio, 1, 256 );

    // Specify viewing/camera/eye coordinate system
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity( );
    // Observer on Y axis, looking at origin, up is Z axis
    gluLookAt( 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 );

    drawWorld();

 
    /*
     * DRAW PERSPECTIVE VIEW
     */
    glViewport(width, height, width, height);

    // Specify projection; this actually specifies the view Volume/
    // viewWindow
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity( );
    gluPerspective( 60, ratio, 1, 256 );

    // Specify viewing/camera/eye coordinate system
    // Observer on Z axis, looking at origin, up is Y axis
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity( );
    gluLookAt( 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 );

    // Add a bit of an angle to get a slightly off center view
    glRotatef(30.0, 0.0, 1.0, 0.0);
    glRotatef(20.0, 1.0, 0.0, 0.0);

    drawWorld();

    // Display new screen
    glutSwapBuffers( );

}


/*
 * Keyboard callback - ESC will terminate program
 */
void keyboard( unsigned char key, int x, int y ) {

    switch ( key ) {
       case 27:
	  exit( 0 );
	  break;
    }

    glutPostRedisplay( );

}


/*
 * Mouse interaction callback function - cursor position
 * is stored
 */

void mouse( int button, int state, int x, int y ) {

    old_x = x;
    old_y = y;

    glutPostRedisplay();

}


/*
 * Mouse motion callback function - new cursor position
 * is used with previous position to define rotation amounts
 */
void motion( int x, int y ) {

    spin_x = x - old_x;
    spin_y = y - old_y;

    glutPostRedisplay( );

}


/*
 * Main program - GLUT setup and initializations
 */
int main( int argc, char** argv ) {

   glutInit( &argc, argv );
   glutInitDisplayMode( GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE );
   glutInitWindowPosition( 50, 50 );
   glutInitWindowSize( 512, 512 );
   glutCreateWindow( "Multiple Views/Multiple Viewports" );

   // Callback functions 
   glutKeyboardFunc( keyboard );
   glutDisplayFunc( display );
   glutMotionFunc( motion );
   glutMouseFunc( mouse );
  
   // Initializations
   init( );

   glutMainLoop( );

   return 0;

}

