This OpenGL program display a 3D solid teapot and allows the user to use both the mouse to move the camera in orbit around the teapot.
teapot2.cpp:
// Alexandri Zavodny
// CSE 40166: Computer Graphics, Fall 2010
// Example 7: Arcball Camera around a teapot
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif
// GLOBAL VARIABLES ////////////////////////////////////////////////////////////
static size_t windowWidth = 640;
static size_t windowHeight = 480;
static float aspectRatio;
GLint leftMouseButton, rightMouseButton; //status of the mouse buttons
int mouseX = 0, mouseY = 0; //last known X and Y of the mouse
float cameraTheta, cameraPhi, cameraRadius; //camera position in spherical coordinates
float x, y, z; //camera position in cartesian coordinates
// recomputeOrientation() //////////////////////////////////////////////////////
//
// This function updates the camera's position in cartesian coordinates based
// on its position in spherical coordinates. Should be called every time
// cameraTheta, cameraPhi, or cameraRadius is updated.
//
////////////////////////////////////////////////////////////////////////////////
void recomputeOrientation()
{
x = cameraRadius * sinf(cameraTheta)*sinf(cameraPhi);
z = cameraRadius * -cosf(cameraTheta)*sinf(cameraPhi);
y = cameraRadius * -cosf(cameraPhi);
glutPostRedisplay();
}
// resizeWindow() //////////////////////////////////////////////////////////////
//
// GLUT callback for window resizing. Resets GL_PROJECTION matrix and viewport.
//
////////////////////////////////////////////////////////////////////////////////
void resizeWindow(int w, int h)
{
aspectRatio = w / (float)h;
windowWidth = w;
windowHeight = h;
//update the viewport to fill the window
glViewport(0, 0, w, h);
//update the projection matrix with the new window properties
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0,aspectRatio,0.1,100000);
}
// mouseCallback() /////////////////////////////////////////////////////////////
//
// GLUT callback for mouse clicks. We save the state of the mouse button
// when this is called so that we can check the status of the mouse
// buttons inside the motion callback (whether they are up or down).
//
////////////////////////////////////////////////////////////////////////////////
void mouseCallback(int button, int state, int thisX, int thisY)
{
//update the left and right mouse button states, if applicable
if(button == GLUT_LEFT_BUTTON)
leftMouseButton = state;
else if(button == GLUT_RIGHT_BUTTON)
rightMouseButton = state;
//and update the last seen X and Y coordinates of the mouse
mouseX = thisX;
mouseY = thisY;
}
// mouseMotion() ///////////////////////////////////////////////////////////////
//
// GLUT callback for mouse movement. We update cameraPhi, cameraTheta, and/or
// cameraRadius based on how much the user has moved the mouse in the
// X or Y directions (in screen space) and whether they have held down
// the left or right mouse buttons. If the user hasn't held down any
// buttons, the function just updates the last seen mouse X and Y coords.
//
////////////////////////////////////////////////////////////////////////////////
void mouseMotion(int x, int y)
{
if(leftMouseButton == GLUT_DOWN)
{
cameraTheta += (x - mouseX)*0.005;
cameraPhi += (y - mouseY)*0.005;
// make sure that phi stays within the range (0, M_PI)
if(cameraPhi <= 0)
cameraPhi = 0+0.001;
if(cameraPhi >= M_PI)
cameraPhi = M_PI-0.001;
recomputeOrientation(); //update camera (x,y,z) based on (radius,theta,phi)
} else if(rightMouseButton == GLUT_DOWN) {
double totalChangeSq = (x - mouseX) + (y - mouseY);
cameraRadius += totalChangeSq*0.01;
//limit the camera radius to some reasonable values so the user can't get lost
if(cameraRadius < 2.0)
cameraRadius = 2.0;
if(cameraRadius > 10.0)
cameraRadius = 10.0;
recomputeOrientation(); //update camera (x,y,z) based on (radius,theta,phi)
}
mouseX = x;
mouseY = y;
}
// initScene() /////////////////////////////////////////////////////////////////
//
// A basic scene initialization function; should be called once after the
// OpenGL context has been created. Doesn't need to be called further.
//
////////////////////////////////////////////////////////////////////////////////
void initScene()
{
glEnable(GL_DEPTH_TEST);
float lightCol[4] = { 1, 1, 1, 1};
float ambientCol[4] = {0.3, 0.3, 0.3, 1.0};
float lPosition[4] = { 10, 10, 10, 1 };
glLightfv(GL_LIGHT0,GL_POSITION,lPosition);
glLightfv(GL_LIGHT0,GL_DIFFUSE,lightCol);
glLightfv(GL_LIGHT0, GL_AMBIENT, ambientCol);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glShadeModel(GL_SMOOTH);
//glShadeModel(GL_FLAT);
}
// renderScene() ///////////////////////////////////////////////////////////////
//
// GLUT callback for scene rendering. Sets up the modelview matrix, renders
// a teapot to the back buffer, and switches the back buffer with the
// front buffer (what the user sees).
//
////////////////////////////////////////////////////////////////////////////////
void renderScene(void)
{
//update the modelview matrix based on the camera's position
glMatrixMode(GL_MODELVIEW); //make sure we aren't changing the projection matrix!
glLoadIdentity();
gluLookAt(x, y, z, //camera is located at (x,y,z)
0, 0, 0, //camera is looking at (0,0,0)
0.0f,1.0f,0.0f); //up vector is (0,1,0) (positive Y)
//clear the render buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//see documentation for glutSolidTeapot; glutSolidTeapot must be called with a different winding set.
//there is a known 'bug' that results in the winding of the teapot to be backwards.
glFrontFace(GL_CW);
glutSolidTeapot(1.0f);
glFrontFace(GL_CCW);
//push the back buffer to the screen
glutSwapBuffers();
}
// normalKeys() ////////////////////////////////////////////////////////////////
//
// GLUT keyboard callback.
//
////////////////////////////////////////////////////////////////////////////////
void normalKeys(unsigned char key, int x, int y)
{
if(key == 'q' || key == 'Q')
exit(0);
}
// main() //////////////////////////////////////////////////////////////////////
//
// Program entry point. Does not process command line arguments.
//
////////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv)
{
//create a double-buffered GLUT window at (50,50) with predefined windowsize
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowPosition(50,50);
glutInitWindowSize(windowWidth,windowHeight);
glutCreateWindow("it's teatime");
//give the camera a 'pretty' starting point!
cameraRadius = 7.0f;
cameraTheta = 2.80;
cameraPhi = 2.0;
recomputeOrientation();
//register callback functions...
glutKeyboardFunc(normalKeys);
glutDisplayFunc(renderScene);
glutReshapeFunc(resizeWindow);
glutMouseFunc(mouseCallback);
glutMotionFunc(mouseMotion);
//do some basic OpenGL setup
initScene();
//and enter the GLUT loop, never to exit.
glutMainLoop();
return(0);
}