327 lines
11 KiB
C++
327 lines
11 KiB
C++
/***********************************************************************
|
|
* Source File:
|
|
* UI INTERACT
|
|
* Author:
|
|
* Br. Helfrich
|
|
* Description:
|
|
* Implement the interfaces specified in uiInterface.h. This handles
|
|
* all the interfaces and events necessary to work with OpenGL. Your
|
|
* program will interface with this thorough the callback function
|
|
* pointer towards the bottom of the file.
|
|
************************************************************************/
|
|
|
|
#include <string> // need you ask?
|
|
#include <sstream> // convert an integer into text
|
|
#include <cassert> // I feel the need... the need for asserts
|
|
#include <time.h> // for clock
|
|
#include <cstdlib> // for rand()
|
|
|
|
|
|
#ifdef __APPLE__
|
|
#include <openGL/gl.h> // Main OpenGL library
|
|
#include <GLUT/glut.h> // Second OpenGL library
|
|
#endif // __APPLE__
|
|
|
|
#ifdef __linux__
|
|
#include <GL/gl.h> // Main OpenGL library
|
|
#include <GL/glut.h> // Second OpenGL library
|
|
#endif // __linux__
|
|
|
|
#ifdef _WIN32
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <Gl/glut.h> // OpenGL library we copied
|
|
#include <ctime> // for ::Sleep();
|
|
#include <Windows.h>
|
|
|
|
#define _USE_MATH_DEFINES
|
|
#include <math.h>
|
|
#endif // _WIN32
|
|
|
|
#include "uiInteract.h"
|
|
#include "point.h"
|
|
|
|
using namespace std;
|
|
|
|
|
|
/*********************************************************************
|
|
* SLEEP
|
|
* Pause for a while. We want to put the program to sleep until it
|
|
* is time to draw again. Note that this requires us to tell the OS
|
|
* that we are idle. the nanosleep function performs this task for us
|
|
* INPUT: msSleep: sleep time in milliseconds
|
|
*********************************************************************/
|
|
void sleep(unsigned long msSleep)
|
|
{
|
|
// Windows handles sleep one way
|
|
#ifdef _WIN32
|
|
::Sleep(msSleep + 35);
|
|
|
|
// Unix-based operating systems (OS-X, Linux) do it another
|
|
#else // LINUX, XCODE
|
|
timespec req = {};
|
|
time_t sec = (int)(msSleep / 1000);
|
|
msSleep -= (sec * 1000);
|
|
|
|
req.tv_sec = sec;
|
|
req.tv_nsec = msSleep * 1000000L;
|
|
|
|
while (nanosleep(&req, &req) == -1)
|
|
;
|
|
#endif // LINUX, XCODE
|
|
return;
|
|
}
|
|
|
|
/************************************************************************
|
|
* DRAW CALLBACK
|
|
* This is the main callback from OpenGL. It gets called constantly by
|
|
* the graphics engine to refresh and draw the window. Here we will
|
|
* clear the background buffer, draw on it, and send it to the forefront
|
|
* when the appropriate time period has passsed.
|
|
*
|
|
* Note: This and all other callbacks can't be member functions, they must
|
|
* have global scope for OpenGL to see them.
|
|
*************************************************************************/
|
|
void drawCallback()
|
|
{
|
|
// even though this is a local variable, all the members are static
|
|
Interface ui;
|
|
// Prepare the background buffer for drawing
|
|
glClear(GL_COLOR_BUFFER_BIT); //clear the screen
|
|
glColor3f(1,1,1);
|
|
|
|
//calls the client's display function
|
|
assert(ui.callBack != NULL);
|
|
ui.callBack(&ui, ui.p);
|
|
|
|
//loop until the timer runs out
|
|
if (!ui.isTimeToDraw())
|
|
sleep((unsigned long)((ui.getNextTick() - clock()) / 1000));
|
|
|
|
// from this point, set the next draw time
|
|
ui.setNextDrawTime();
|
|
|
|
// bring forth the background buffer
|
|
glutSwapBuffers();
|
|
|
|
// clear the space at the end
|
|
ui.keyEvent();
|
|
}
|
|
|
|
/************************************************************************
|
|
* KEY DOWN CALLBACK
|
|
* When a key on the keyboard has been pressed, we need to pass that
|
|
* on to the client. Currently, we are only registering the arrow keys
|
|
* INPUT key: the key we pressed according to the GLUT_KEY_ prefix
|
|
* x y: the position in the window, which we ignore
|
|
*************************************************************************/
|
|
void keyDownCallback(int key, int x, int y)
|
|
{
|
|
// Even though this is a local variable, all the members are static
|
|
// so we are actually getting the same version as in the constructor.
|
|
Interface ui;
|
|
ui.keyEvent(key, true /*fDown*/);
|
|
}
|
|
|
|
/************************************************************************
|
|
* KEY UP CALLBACK
|
|
* When the user has released the key, we need to reset the pressed flag
|
|
* INPUT key: the key we pressed according to the GLUT_KEY_ prefix
|
|
* x y: the position in the window, which we ignore
|
|
*************************************************************************/
|
|
void keyUpCallback(int key, int x, int y)
|
|
{
|
|
// Even though this is a local variable, all the members are static
|
|
// so we are actually getting the same version as in the constructor.
|
|
Interface ui;
|
|
ui.keyEvent(key, false /*fDown*/);
|
|
}
|
|
|
|
/***************************************************************
|
|
* KEYBOARD CALLBACK
|
|
* Generic callback to a regular ascii keyboard event, such as
|
|
* the space bar or the letter 'q'
|
|
***************************************************************/
|
|
void keyboardCallback(unsigned char key, int x, int y)
|
|
{
|
|
// Even though this is a local variable, all the members are static
|
|
// so we are actually getting the same version as in the constructor.
|
|
Interface ui;
|
|
ui.keyEvent(key, true /*fDown*/);
|
|
}
|
|
|
|
/***************************************************************
|
|
* INTERFACE : KEY EVENT
|
|
* Either set the up or down event for a given key
|
|
* INPUT key which key is pressed
|
|
* fDown down or brown
|
|
****************************************************************/
|
|
void Interface::keyEvent(int key, bool fDown)
|
|
{
|
|
switch(key)
|
|
{
|
|
case GLUT_KEY_DOWN:
|
|
isDownPress = fDown;
|
|
break;
|
|
case GLUT_KEY_UP:
|
|
isUpPress = fDown;
|
|
break;
|
|
case GLUT_KEY_RIGHT:
|
|
isRightPress = fDown;
|
|
break;
|
|
case GLUT_KEY_LEFT:
|
|
isLeftPress = fDown;
|
|
break;
|
|
case GLUT_KEY_HOME:
|
|
case ' ':
|
|
isSpacePress = fDown;
|
|
break;
|
|
}
|
|
}
|
|
/***************************************************************
|
|
* INTERFACE : KEY EVENT
|
|
* Either set the up or down event for a given key
|
|
* INPUT key which key is pressed
|
|
* fDown down or brown
|
|
****************************************************************/
|
|
void Interface::keyEvent()
|
|
{
|
|
if (isDownPress)
|
|
isDownPress++;
|
|
if (isUpPress)
|
|
isUpPress++;
|
|
if (isLeftPress)
|
|
isLeftPress++;
|
|
if (isRightPress)
|
|
isRightPress++;
|
|
isSpacePress = false;
|
|
}
|
|
|
|
|
|
/************************************************************************
|
|
* INTEFACE : IS TIME TO DRAW
|
|
* Have we waited long enough to draw swap the background buffer with
|
|
* the foreground buffer?
|
|
*************************************************************************/
|
|
bool Interface::isTimeToDraw()
|
|
{
|
|
return ((unsigned int)clock() >= nextTick);
|
|
}
|
|
|
|
/************************************************************************
|
|
* INTERFACE : SET NEXT DRAW TIME
|
|
* What time should we draw the buffer again? This is a function of
|
|
* the current time and the frames per second.
|
|
*************************************************************************/
|
|
void Interface::setNextDrawTime()
|
|
{
|
|
nextTick = clock() + static_cast<int> (timePeriod * CLOCKS_PER_SEC);
|
|
}
|
|
|
|
/************************************************************************
|
|
* INTERFACE : SET FRAMES PER SECOND
|
|
* The frames per second dictates the speed of the game. The more frames
|
|
* per second, the quicker the game will appear to the user. We will default
|
|
* to 30 frames/second but the client can set this at will.
|
|
* INPUT value The number of frames per second. 30 is default
|
|
*************************************************************************/
|
|
void Interface::setFramesPerSecond(double value)
|
|
{
|
|
timePeriod = (1 / value);
|
|
}
|
|
|
|
/***************************************************
|
|
* STATICS
|
|
* All the static member variables need to be initialized
|
|
* Somewhere globally. This is a good spot
|
|
**************************************************/
|
|
int Interface::isDownPress = 0;
|
|
int Interface::isUpPress = 0;
|
|
int Interface::isLeftPress = 0;
|
|
int Interface::isRightPress = 0;
|
|
bool Interface::isSpacePress = false;
|
|
bool Interface::initialized = false;
|
|
double Interface::timePeriod = 1.0 / 30; // default to 30 frames/second
|
|
unsigned int Interface::nextTick = 0; // redraw now please
|
|
void * Interface::p = NULL;
|
|
void (*Interface::callBack)(const Interface *, void *) = NULL;
|
|
|
|
|
|
/************************************************************************
|
|
* INTERFACE : DESTRUCTOR
|
|
* Nothing here!
|
|
***********************************************************************/
|
|
Interface::~Interface()
|
|
{
|
|
}
|
|
|
|
|
|
/************************************************************************
|
|
* INTEFACE : INITIALIZE
|
|
* Initialize our drawing window. This will set the size and position,
|
|
* get ready for drawing, set up the colors, and everything else ready to
|
|
* draw the window. All these are part of initializing Open GL.
|
|
* INPUT argc: Count of command-line arguments from main
|
|
* argv: The actual command-line parameters
|
|
* title: The text for the titlebar of the window
|
|
*************************************************************************/
|
|
void Interface::initialize(int argc, char ** argv, const char * title, Point topLeft, Point bottomRight)
|
|
{
|
|
if (initialized)
|
|
return;
|
|
|
|
// set up the random number generator
|
|
srand((unsigned int)time(NULL));
|
|
|
|
// create the window
|
|
glutInit(&argc, argv);
|
|
Point point;
|
|
glutInitWindowSize( // size of the window
|
|
(int)(bottomRight.getX() - topLeft.getX()),
|
|
(int)(topLeft.getY() - bottomRight.getY()));
|
|
|
|
glutInitWindowPosition( 10, 10); // initial position
|
|
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); // double buffering
|
|
glutCreateWindow(title); // text on titlebar
|
|
glutIgnoreKeyRepeat(true);
|
|
|
|
// set up the drawing style: B/W and 2D
|
|
glClearColor(0, 0, 0, 0); // Black is the background color
|
|
gluOrtho2D((int)topLeft.getX(), (int)bottomRight.getX(),
|
|
(int)bottomRight.getY(), (int)topLeft.getY()); // 2D environment
|
|
|
|
// register the callbacks so OpenGL knows how to call us
|
|
glutDisplayFunc( drawCallback );
|
|
glutIdleFunc( drawCallback );
|
|
glutKeyboardFunc( keyboardCallback);
|
|
glutSpecialFunc( keyDownCallback );
|
|
glutSpecialUpFunc( keyUpCallback );
|
|
initialized = true;
|
|
|
|
// done
|
|
return;
|
|
}
|
|
|
|
/************************************************************************
|
|
* INTERFACE : RUN
|
|
* Start the main graphics loop and play the game
|
|
* INPUT callBack: Callback function. Every time we are beginning
|
|
* to draw a new frame, we first callback to the client
|
|
* to see if he wants to do anything, such as move
|
|
* the game pieces or respond to input
|
|
* p: Void point to whatever the caller wants. You
|
|
* will need to cast this back to your own data
|
|
* type before using it.
|
|
*************************************************************************/
|
|
void Interface::run(void (*callBack)(const Interface *, void *), void *p)
|
|
{
|
|
// setup the callbacks
|
|
this->p = p;
|
|
this->callBack = callBack;
|
|
|
|
glutMainLoop();
|
|
|
|
return;
|
|
}
|
|
|