

//
// This code was created by Jeff Molofee '99
//
// If you've found this code useful, please let me know.
//
// Visit me at www.demonews.com/hosted/nehe
//
/**************************************************************/
// This code was ported to MacOS by Tony Parker.
//  I'd also appreciate it if you could drop me a line if you found
//  this code useful. 
// 
//  Tony Parker - asp@usc.edu
// 
// Have a nice day.

// Mac OS X Conversion by Chad Armstrong (chad@edenwaith.com)
// 1. Add these three Frameworks to the External Frameworks folder
//    Foundation
//    GLUT
//    OpenGL
// 2. Change the header <glut.h> to <GLUT/glut.h>
// 3. Comment out the <gl.h> and <glu.h> headers

#include <iostream.h>			// Header File For Standard C++ Input / Output
#include <stdio.h>			// Header File For Standard C Input / Output
#include <stdarg.h>			// Header File For Variable Argument Routines
#include <string.h>			// Header File For String Management
#include <stdlib.h>
//#include <gl.h>				// Header File For The OpenGL32 Library
//#include <glu.h>			// Header File For The GLu32 Library
#include <GLUT/glut.h>			// Header File For The GLUT Library
#include <math.h>

// Constants -----------------------------------------------------------------

#define kWindowWidth	512
#define kWindowHeight	256
#define PYRAMID		100
#define CUBE		101
#define TORUS		102

int window;

// Function Prototypes -------------------------------------------------------

GLvoid InitGL(GLvoid);
GLvoid drawShapes(GLenum mode);
GLvoid display(GLvoid);
GLvoid ReSizeGLScene(int Width, int Height);
GLvoid Idle(GLvoid);
void keyboard(unsigned char key, int x, int y);
void mouse_function(int button, int state, int x, int y);
int retrieveObjectID(int x, int y);

// Global Variables ----------------------------------------------------------

GLfloat	rtri;			// Angle For The Triangle
GLfloat	rquad;			// Angle For The Quad
int objectID;

// Main ----------------------------------------------------------------------

int main(int argc, char** argv)
{
    
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(kWindowWidth, kWindowHeight); 
    glutInitWindowPosition (100, 100);
    window = glutCreateWindow (argv[0]);
	
	InitGL();
 
    glutDisplayFunc(display); 
    glutReshapeFunc(ReSizeGLScene);
    glutIdleFunc(Idle);
    glutKeyboardFunc(keyboard);
    glutMouseFunc(mouse_function);

    glutMainLoop();
    
    rtri = 0;
    rquad = 0;
    return 0;
}

// InitGL --------------------------------------------------------------------

GLvoid InitGL(GLvoid)
{
    objectID = 0; // this isn't OpenGL related per se, but it needs to be initialized

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);		// This Will Clear The Background Color To Black
    glClearDepth(1.0);							// Enables Clearing Of The Depth Buffer
    glDepthFunc(GL_LESS);						// The Type Of Depth Test To Do
    glEnable(GL_DEPTH_TEST);					// Enables Depth Testing 
    glShadeModel(GL_SMOOTH);					// Enables Smooth Color Shading

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();							// Reset The Projection Matrix

    gluPerspective(45.0f, (GLfloat) kWindowWidth / (GLfloat) kWindowHeight, 0.1f, 100.0f);	
												// Calculate The Aspect Ratio Of The Window

    glMatrixMode(GL_MODELVIEW);

}

// Idle ----------------------------------------------------------------------

GLvoid Idle(GLvoid)
{
	rtri += 0.2f;							// Increase The Rotation Variable For The Triangle 
	rquad -= 0.15f;							// Decrease The Rotation Variable For The Quad 
	
	glutPostRedisplay();
}

// drawShapes ---------------------------------------------------------------
GLvoid drawShapes(GLenum mode)
{
        // these two lines were instrumental in getting the picking to work!
        // two lines!  That's it!
        glInitNames();
        glPushName(0);

	glLoadIdentity();									// Reset The View
	glTranslatef(-1.5f,0.0f,-6.0f);				// Move Left And Into The Screen

	glRotatef(rtri,0.0f,1.0f,0.0f);				// Rotate The Pyramid On It's Y Axis
        if (GL_SELECT == mode)
        {
            glLoadName(PYRAMID);
        }

	glBegin(GL_POLYGON);					// Start Drawing The Pyramid						
		glColor3f(1.0f,0.0f,0.0f);				// Red
		glVertex3f( 0.0f, 1.0f, 0.0f);			// Top Of Triangle (Front)
		glColor3f(0.0f,1.0f,0.0f);				// Green
		glVertex3f(-1.0f,-1.0f, 1.0f);			// Left Of Triangle (Front)
		glColor3f(0.0f,0.0f,1.0f);				// Blue
		glVertex3f( 1.0f,-1.0f, 1.0f);			// Right Of Triangle (Front)
	
		glColor3f(1.0f,0.0f,0.0f);				// Red
		glVertex3f( 0.0f, 1.0f, 0.0f);			// Top Of Triangle (Right)
		glColor3f(0.0f,0.0f,1.0f);				// Blue
		glVertex3f( 1.0f,-1.0f, 1.0f);			// Left Of Triangle (Right)
		glColor3f(0.0f,1.0f,0.0f);				// Green
		glVertex3f( 1.0f,-1.0f, -1.0f);			// Right Of Triangle (Right)
		
		glColor3f(1.0f,0.0f,0.0f);				// Red
		glVertex3f( 0.0f, 1.0f, 0.0f);			// Top Of Triangle (Back)
		glColor3f(0.0f,1.0f,0.0f);				// Green
		glVertex3f( 1.0f,-1.0f, -1.0f);			// Left Of Triangle (Back)
		glColor3f(0.0f,0.0f,1.0f);				// Blue
		glVertex3f(-1.0f,-1.0f, -1.0f);			// Right Of Triangle (Back)

		glColor3f(1.0f,0.0f,0.0f);				// Red
		glVertex3f( 0.0f, 1.0f, 0.0f);			// Top Of Triangle (Left)
		glColor3f(0.0f,0.0f,1.0f);				// Blue
		glVertex3f(-1.0f,-1.0f,-1.0f);			// Left Of Triangle (Left)
		glColor3f(0.0f,1.0f,0.0f);				// Green
		glVertex3f(-1.0f,-1.0f, 1.0f);			// Right Of Triangle (Left)
	glEnd();								// Done Drawing Pyramid
	
	glLoadIdentity();
	glTranslatef(1.5f,0.0f,-7.0f);				// Move Right And Into The Screen

	glRotatef(rquad,1.0f,1.0f,1.0f);			// Rotate The Cube On X, Y & Z

        if (GL_SELECT == mode)
        {
            glLoadName(CUBE);
        }

	glBegin(GL_QUADS);						// Start Drawing The Cube
		glColor3f(0.0f,1.0f,0.0f);				// Set The Color To Blue
		glVertex3f( 1.0f, 1.0f,-1.0f);			// Top Right Of The Quad (Top)
		glVertex3f(-1.0f, 1.0f,-1.0f);			// Top Left Of The Quad (Top)
		glVertex3f(-1.0f, 1.0f, 1.0f);			// Bottom Left Of The Quad (Top)
		glVertex3f( 1.0f, 1.0f, 1.0f);			// Bottom Right Of The Quad (Top)
		
		glColor3f(1.0f,0.5f,0.0f);				// Set The Color To Orange
		glVertex3f( 1.0f,-1.0f, 1.0f);			// Top Right Of The Quad (Bottom)
		glVertex3f(-1.0f,-1.0f, 1.0f);			// Top Left Of The Quad (Bottom)
		glVertex3f(-1.0f,-1.0f,-1.0f);			// Bottom Left Of The Quad (Bottom)
		glVertex3f( 1.0f,-1.0f,-1.0f);			// Bottom Right Of The Quad (Bottom)
		
		glColor3f(1.0f,0.0f,0.0f);				// Set The Color To Red
		glVertex3f( 1.0f, 1.0f, 1.0f);			// Top Right Of The Quad (Front)
		glVertex3f(-1.0f, 1.0f, 1.0f);			// Top Left Of The Quad (Front)
		glVertex3f(-1.0f,-1.0f, 1.0f);			// Bottom Left Of The Quad (Front)
		glVertex3f( 1.0f,-1.0f, 1.0f);			// Bottom Right Of The Quad (Front)
		
		glColor3f(1.0f,1.0f,0.0f);				// Set The Color To Yellow
		glVertex3f( 1.0f,-1.0f,-1.0f);			// Top Right Of The Quad (Back)
		glVertex3f(-1.0f,-1.0f,-1.0f);			// Top Left Of The Quad (Back)
		glVertex3f(-1.0f, 1.0f,-1.0f);			// Bottom Left Of The Quad (Back)
		glVertex3f( 1.0f, 1.0f,-1.0f);			// Bottom Right Of The Quad (Back)
    
    	glColor3f(0.0f,0.0f,1.0f);				// Set The Color To Blue
		glVertex3f(-1.0f, 1.0f, 1.0f);			// Top Right Of The Quad (Left)
		glVertex3f(-1.0f, 1.0f,-1.0f);			// Top Left Of The Quad (Left)
		glVertex3f(-1.0f,-1.0f,-1.0f);			// Bottom Left Of The Quad (Left)
		glVertex3f(-1.0f,-1.0f, 1.0f);			// Bottom Right Of The Quad (Left)
		
		glColor3f(1.0f,0.0f,1.0f);				// Set The Color To Violet
		glVertex3f( 1.0f, 1.0f,-1.0f);			// Top Right Of The Quad (Right)
		glVertex3f( 1.0f, 1.0f, 1.0f);			// Top Left Of The Quad (Right)
		glVertex3f( 1.0f,-1.0f, 1.0f);			// Bottom Left Of The Quad (Right)
		glVertex3f( 1.0f,-1.0f,-1.0f);			// Bottom Right Of The Quad (Right)
	glEnd();								// Done Drawing The Quad
        
        glLoadIdentity();
	glTranslatef(0.5f,0.0f,-7.0f);
        glRotatef(rquad,1.0f,0.0f,0.0f);
        
        if (GL_SELECT == mode)
        {
            glLoadName(TORUS);
        }
        
        glColor3f(0.0f,0.0f,1.0f);
        glutSolidTorus(0.5, 3.0, 20, 20);

}

// DrawGLScene ---------------------------------------------------------------

GLvoid display(GLvoid)
{    
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear The Screen And The Depth Buffer
    drawShapes(GL_RENDER);
	
    glutSwapBuffers();
    glFlush();
}

// ReSizeGLScene ------------------------------------------------------------

GLvoid ReSizeGLScene(int Width, int Height)
{
    glViewport (0, 0, (GLsizei) Width, (GLsizei) Height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    gluPerspective(45.0, (GLfloat) Width / (GLfloat) Height, 0.1, 100.0);
   
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

// keyboard  ------------------------------------------------------------
void keyboard(unsigned char key, int x, int y)
{
   switch (key) 
   {
      case 27: // The ESC key
         // glutLeaveGameMode();
         glutDestroyWindow(window);
         exit(0);
         break;
      default:
        printf("%c\n", key);
        break;
    }
}

// mouse_function ------------------------------------------------------------
void mouse_function(int button, int state, int x, int y)
{
    if (state == GLUT_DOWN)
    {
        objectID = retrieveObjectID(x, y);
    
        switch (objectID)
        {
            case PYRAMID: printf("Saw the pyramid\n"); break;
            case CUBE: printf("Saw the cube\n"); break;
            case TORUS: printf("Saw the torus\n"); break;
            default: printf("I didn't see nothing!\n"); break;
        }
    }
}

// ===================================================================
// int retrieveObjectID(int x, int y)
// -------------------------------------------------------------------
// Version: 23. January 2003 22:51
// Created: 23. January 2003 22:51
// ===================================================================
int retrieveObjectID(int x, int y)
{
    int objectsFound = 0;
    GLint viewportCoords[4] = {0};
    GLuint selectBuffer[32] = {0};
    
    glSelectBuffer(32, selectBuffer);
    glGetIntegerv(GL_VIEWPORT, viewportCoords);
    glMatrixMode(GL_PROJECTION);
    
    glPushMatrix();
        glRenderMode(GL_SELECT);
        glLoadIdentity();
        gluPickMatrix(x, viewportCoords[3] - y, 2, 2, viewportCoords);
    
        gluPerspective(45.0f, (float)kWindowWidth/(float)kWindowHeight, 0.1f, 150.0f);
        glMatrixMode(GL_MODELVIEW);
    
        //DrawGLScene(); // ??
        drawShapes(GL_SELECT);
    
        objectsFound = glRenderMode(GL_RENDER);
    
        glMatrixMode(GL_PROJECTION);
        
    glPopMatrix();
    
    glMatrixMode(GL_MODELVIEW);
    
    if (objectsFound > 0)
    {
        GLuint lowestDepth = selectBuffer[1];
        
        int selectedObject = selectBuffer[3];
        
        for (int i = 1; i < objectsFound; i++)
        {
            if (selectBuffer[(i*4)+1] < lowestDepth)
            {
                lowestDepth = selectBuffer[(i*4)+1];
                selectedObject = selectBuffer[(i*4)+3];
            }
        }
        
        return selectedObject;
    
    }
    
    
    return 0;
}
