// opengl.cpp

#include "stdafx.h"
#include <glut.h>
#include "nsbmd.h"

/*------------------------------------------------------------
	OpenGL - Mouse callback function
------------------------------------------------------------*/
static BOOL mouse_r = FALSE;
static BOOL mouse_l = FALSE;
static int mouse_x = 0;
static int mouse_y = 0;

void mouse_func ( int button, int state, int x, int y )
{
	mouse_x = x;
	mouse_y = y;
	if ( button == GLUT_RIGHT_BUTTON )
		mouse_r = (state == GLUT_DOWN);
	else if ( button == GLUT_LEFT_BUTTON )
		mouse_l = (state == GLUT_DOWN);
}

/*------------------------------------------------------------
	OpenGL - Motion callback function
------------------------------------------------------------*/
static float ang = 0.0f;
static float elev = 0.0f;
static float dist = 2.5f;

void motion_func ( int x, int y )
{
	int dx = x - mouse_x;
	int dy = y - mouse_y;

	if ( mouse_l && (dx || dy) ) {
		if( dx || dy ) {
			ang += dx;
			elev += dy;
			//if ( elev > 90.0f )	elev = 90.0f;
			if ( elev > 360.0f )	elev = 0.0f;

			//if ( elev < -90.0f )	elev = -90.0f;
			if ( elev < -360.0f )	elev = 0.0f;
			glutPostRedisplay();
		}
	}

	if ( mouse_r ) {
		dist += dy*0.01f;
		if ( dist < 0.0f )	dist = 0.0f;
		//if ( dist > 32.0f ) 	dist = 32.0f;
		glutPostRedisplay();
	}

	mouse_x = x;
	mouse_y = y;
}

/*------------------------------------------------------------
	OpenGL - Keyboard callback function
------------------------------------------------------------*/
void kb_func ( u8 key, int x, int y )
{
	switch ( key ) {
	case 't':
	case 'T':
		gOptTexture = !gOptTexture;
		printf( "\tTexturing %s\n", gOptTexture ? "ON":"OFF" );
		glutPostRedisplay();
		break;
	case 'c':
	case 'C':
		gOptColoring = !gOptColoring;
		printf( "\tColouring %s\n", gOptColoring ? "ON":"OFF" );
		glutPostRedisplay();
		break;
	case 'w':
	case 'W':
		gOptWireFrame = !gOptWireFrame;
		printf( "\tWireframe %s\n", gOptWireFrame ? "ON":"OFF" );
		glPolygonMode( GL_FRONT_AND_BACK, gOptWireFrame ? GL_LINE : GL_FILL );
		glutPostRedisplay();
		break;
	case 'b':
	case 'B':
		gOptCulling = !gOptCulling;
		printf( "\tCulling %s\n", gOptCulling ? "ON":"OFF" );
		(gOptCulling ? glEnable : glDisable)( GL_CULL_FACE );
		glutPostRedisplay();
		break;
	case 'f':
	case 'F':
		gOptFiltering = !gOptFiltering;
		printf( "\tTexture Filtering %s\n", gOptFiltering ? "ON":"OFF" );
		for ( int m = 0; m < g_matnum; m++ )
		{
			glBindTexture( GL_TEXTURE_2D, m+1 );
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gOptFiltering ? GL_LINEAR : GL_NEAREST );
			glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gOptFiltering ? GL_LINEAR : GL_NEAREST );
		}
		glutPostRedisplay();
		break;
	case 'p':
	case 'P':
		gOptPolyMode = !gOptPolyMode;
		printf( "\tPolygon Mode %s\n", gOptPolyMode ? "ON":"OFF" );
		glutPostRedisplay();
		break;
	case 'V':
	case 'v':
		gOptVertexMode = !gOptVertexMode;
		gOptPolyMode = gOptVertexMode;
		printf( "\tVertex Mode %s\n", gOptVertexMode ? "ON":"OFF" );
		printf( "\tPolygon Mode %s\n", gOptPolyMode ? "ON":"OFF" );
		glutPostRedisplay();
	}
}

/*------------------------------------------------------------
	OpenGL - Special keys callback function
------------------------------------------------------------*/
void sp_func ( int key, int x, int y )
{
	switch ( key ) {
	// select previous model
	case GLUT_KEY_HOME:
		gCurrentModel--;
		if ( gCurrentModel < 0 ) gCurrentModel = g_modelnum - 1;
		if ( gCurrentPoly >= g_model[ gCurrentModel ].polynum ) gCurrentPoly = g_model[ gCurrentModel ].polynum - 1;
		if ( gOptPolyMode )
			printf( "\tModel %d Polygon %d\n", gCurrentModel, gCurrentPoly );
		else
			printf( "\tModel %d\n", gCurrentModel );
		glutPostRedisplay();
		break;
	// select next model
	case GLUT_KEY_END:
		gCurrentModel++;
		if ( gCurrentModel >= g_modelnum ) gCurrentModel = 0;
		if ( gCurrentPoly >= g_model[ gCurrentModel ].polynum ) gCurrentPoly = g_model[ gCurrentModel ].polynum - 1;
		if ( gOptPolyMode )
			printf( "\tModel %d Polygon %d\n", gCurrentModel, gCurrentPoly );
		else
			printf( "\tModel %d\n", gCurrentModel );
		glutPostRedisplay();
		break;
	// select next polygon
	case GLUT_KEY_PAGE_DOWN:
		if ( gOptPolyMode ) {
			gCurrentPoly++;
			if ( gCurrentPoly >= g_model[ gCurrentModel ].polynum ) gCurrentPoly = 0;
			printf( "\tModel %d Polygon %d\n", gCurrentModel, gCurrentPoly );
			glutPostRedisplay();
		}
		break;		
	// select previous polygon
	case GLUT_KEY_PAGE_UP:
		if ( gOptPolyMode ) {
			gCurrentPoly--;
			if ( gCurrentPoly < 0 ) gCurrentPoly = g_model[ gCurrentModel ].polynum - 1;
			printf( "\tModel %d Polygon %d\n", gCurrentModel, gCurrentPoly );
			glutPostRedisplay();
		}
		break;
	case GLUT_KEY_F1:
		if ( gOptVertexMode ) {
			gCurrentVertex++;
			printf( "\tModel %d Polygon %d Vertex %d\n", gCurrentModel, gCurrentPoly, gCurrentVertex );
			glutPostRedisplay();
		}
		break;
	case GLUT_KEY_F2:
		if ( gOptVertexMode ) {
			gCurrentVertex--;
			if ( gCurrentVertex < 0 ) gCurrentVertex = 0;
			printf( "\tModel %d Polygon %d Vertex %d\n", gCurrentModel, gCurrentPoly, gCurrentVertex );
			glutPostRedisplay();
		}
		break;
	case GLUT_KEY_F5:
		gOptGrid = !gOptGrid;
		glutPostRedisplay();
		break;
//	case GLUT_KEY_DOWN:
//	case GLUT_KEY_UP:
//	case GLUT_KEY_RIGHT:
//	case GLUT_KEY_LEFT:
//		break;
	}
}







/*------------------------------------------------------------
	OpenGL - Display callback function
------------------------------------------------------------*/
void display_func ( void )
{
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	GLfloat vp[4];
	glGetFloatv( GL_VIEWPORT, vp );
	float aspect = (vp[2]-vp[0]) / (vp[3]-vp[1]);

	glMatrixMode( GL_PROJECTION );
	glLoadIdentity();
	gluPerspective( 90.0f, aspect, 0.02f, 32.0f );

	// Applies subsequent matrix operations to the modelview matrix stack. 
	// In GL_MODELVIEW mode, the stack depth is at least 32. 
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
	glTranslatef( 0.0f, 0.0f, -dist );
	glRotatef( elev, 1, 0, 0 );
	glRotatef( ang, 0, 1, 0 );

	if ( gOptGrid ) {
		glBindTexture( GL_TEXTURE_2D, 0 );
		glBegin( GL_LINES );
		glColor3f( 1.0f, 1.0f, 1.0f );
		glVertex3f( 0.0f, 0.0f, 0.0f );
		glVertex3f( 1.0f, 0.0f, 0.0f );
		glVertex3f( 0.0f, 0.0f, 0.0f );
		glVertex3f( 0.0f, 1.0f, 0.0f );
		glVertex3f( 0.0f, 0.0f, 0.0f );
		glVertex3f( 0.0f, 0.0f, 1.0f );
		glEnd();
		glRasterPos3f( 1.0f, 0.0f, 0.0f );
		glutBitmapCharacter( GLUT_BITMAP_HELVETICA_18, 'X' );
		glRasterPos3f( 0.0f, 1.0f, 0.0f );
		glutBitmapCharacter( GLUT_BITMAP_HELVETICA_18, 'Y' );
		glRasterPos3f( 0.0f, 0.0f, 1.0f );
		glutBitmapCharacter( GLUT_BITMAP_HELVETICA_18, 'Z' );
	}

	render_scene();

	glutSwapBuffers();
}