Original Post
Hey guys. I wrote the following code that generates a clock. I'm reading system time, calculating angle for minute/hour hand and rotating them accordingly. My problem is that when i attempt to rotate my viewing volume, my minute hand keeps rotating around the z-axis and goes inside the clock. This happens with the minute hand only. When i remove glLoadIdentity() before i start drawing the minute hand, it appears okay, but my minute hand is no longer displaying at the right angle. This is why i initially added glLoadIdentity() because i figured something must be accumulating in the matrix. Does anybody have any suggestions? Thanks!
#include <gl/glut.h>
#include <iostream>
#include <math.h>
#include <time.h>
// Function prototypes
void mDisplay();
void mReshape(int prmWidth, int prmHeight);
void mInit();
void mTimer(int value);
void mCalculateTime();
void mKey(unsigned char prmKey, int prmX, int prmY);
void mDrawFillCircle(GLfloat prmX,
GLfloat prmY,
GLfloat prmRadius);
void mDrawCylinder(GLfloat prmArc,
GLfloat prmRadius,
GLfloat prmHeight,
GLint prmSlices,
GLint prmStacks);
// Enable/Disable wireframe view
const bool USE_WIREFRAME = false;
// Initial minute/hour hand angles
float thetaHour = 90;
float thetaMin = 90;
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(600, 600);
glutCreateWindow("Clock");
mInit();
mCalculateTime();
glutTimerFunc(10, mTimer, 0);
glutDisplayFunc(mDisplay);
glutReshapeFunc(mReshape);
glutKeyboardFunc(mKey);
glutMainLoop();
return 0;
}
/**
* mTimer()
* Precondition: value != null
* Postcondition: mCalculateTime() is called.
*/
void mTimer(int value)
{
glLoadIdentity();
mCalculateTime();
mDisplay();
glutTimerFunc(10, mTimer, 0);
glutPostRedisplay();
}
/**
* mCalculateTime()
* Precondition: none
* Postcondition: System time is retrieved and the new minute/hour
* hand angles are calculated.
*/
void mCalculateTime()
{
time_t tval = time(0);
struct tm *tm_struct = localtime(&tval);
thetaHour = 30 * tm_struct->tm_hour + tm_struct->tm_min / 2 + tm_struct->tm_sec / 120;
thetaMin = 6 * tm_struct->tm_min + tm_struct->tm_sec / 10;
}
/**
* mDisplay()
* Precondition: none
* Postcondition: Clock is drawn.
*/
void mDisplay()
{
// Initialize viewing volume
glClearColor(0,0,0,0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
// Draw bottom disk
glColor3d(0.1,0.4,0.6);
mDrawFillCircle(0.0,0.0,300.0);
// Draw cylinder sides
glColor3d(0.8,0.1,0.6);
mDrawCylinder(360.0,300.0,76.0,40.0,40.0);
// Move to the top of the cylinder
glTranslatef(0.0, 0.0, 76.0);
// Draw top disk
glColor3d(0.4,0.3,0.4);
mDrawFillCircle(0.0,0.0,300.0);
// Rotate hour hand
glRotatef(-thetaHour,0.0,0.0,1.0);
// Draw hour hand
glBegin(GL_QUADS);
// Front (center)
glColor3f(0.0,1.0,0.0);
glVertex3f(-5.0,0.0,-80.0);
glVertex3f(5.0,0.0,-80.0);
glVertex3f(5.0,0.0,-86.0);
glVertex3f(-5.0,0.0,-86.0);
// Right side
glColor3f(0.0,1.0,0.0);
glVertex3f(5.0,0.0,-86.0);
glVertex3f(5.0,0.0,-80.0);
glVertex3f(5.0,143.0,-80.0);
glVertex3f(5.0,143.0,-86.0);
// Front (far)
glColor3f(0.0,1.0,0.0);
glVertex3f(5.0,143.0,-80.0);
glVertex3f(-5.0,143.0,-80.0);
glVertex3f(-5.0,143.0,-86.0);
glVertex3f(5.0,143.0,-86.0);
// Left side
glColor3f(0.0,1.0,0.0);
glVertex3f(-5.0,143.0,-80.0);
glVertex3f(-5.0,0.0,-80.0);
glVertex3f(-5.0,0.0,-86.0);
glVertex3f(-5.0,143.0,-86.0);
// Bottom
glColor3f(0.0,1.0,0.0);
glVertex3f(5.0,0.0,-80.0);
glVertex3f(-5.0,0.0,-80.0);
glVertex3f(-5.0,143.0,-80.0);
glVertex3f(5.0,143.0,-80.0);
// Top
glColor3f(0.0,1.0,0.0);
glVertex3f(5.0,0.0,-86.0);
glVertex3f(-5.0,0.0,-86.0);
glVertex3f(-5.0,143.0,-86.0);
glVertex3f(5.0,143.0,-86.0);
glEnd();
// Move 10 units up
glTranslatef(0.0,0.0,-10);
// Clear identity matrix
glLoadIdentity();
// Rotate minute hand
glRotatef(-thetaMin,0.0,0.0,1.0);
// Draw minute hand
glBegin(GL_QUADS);
// Front (center)
glColor3f(0.0,1.0,0.0);
glVertex3f(-5.0,0.0,-80.0);
glVertex3f(5.0,0.0,-80.0);
glVertex3f(5.0,0.0,-86.0);
glVertex3f(-5.0,0.0,-86.0);
// Right side
glColor3f(0.0,1.0,0.0);
glVertex3f(5.0,0.0,-86.0);
glVertex3f(5.0,0.0,-80.0);
glVertex3f(5.0,212.0,-80.0);
glVertex3f(5.0,212.0,-86.0);
// Front (far)
glColor3f(0.0,1.0,0.0);
glVertex3f(5.0,212.0,-80.0);
glVertex3f(-5.0,212.0,-80.0);
glVertex3f(-5.0,212.0,-86.0);
glVertex3f(5.0,212.0,-86.0);
// Left side
glColor3f(0.0,1.0,0.0);
glVertex3f(-5.0,212.0,-80.0);
glVertex3f(-5.0,0.0,-80.0);
glVertex3f(-5.0,0.0,-86.0);
glVertex3f(-5.0,212.0,-86.0);
// Bottom
glColor3f(0.0,1.0,0.0);
glVertex3f(5.0,0.0,-80.0);
glVertex3f(-5.0,0.0,-80.0);
glVertex3f(-5.0,212.0,-80.0);
glVertex3f(5.0,212.0,-80.0);
// Top
glColor3f(0.0,1.0,0.0);
glVertex3f(5.0,0.0,-86.0);
glVertex3f(-5.0,0.0,-86.0);
glVertex3f(-5.0,212.0,-86.0);
glVertex3f(5.0,212.0,-86.0);
glEnd();
glFlush();
glutSwapBuffers();
}
/**
* mReshape()
* Precondition: prmWidth > 0 and prmHeight > 0
* Postcondition: Clock is redrawn on window resize event.
*/
void mReshape(int prmWidth, int prmHeight)
{
glViewport(0, 0, prmWidth, prmWidth);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-300.0,300.0,-300.0,300.0,300.0,-300.0);
glMatrixMode(GL_MODELVIEW);
}
/**
* mInit()
* Precondition: none
* Postcondition: Viewing volume is initialized.
*/
void mInit()
{
if (USE_WIREFRAME)
{
glPolygonMode(GL_FRONT, GL_LINE);
glPolygonMode(GL_BACK, GL_LINE);
}
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-300,300,-300,300,300,-300);
glMatrixMode(GL_MODELVIEW);
}
/**
* mKey()
* Precondition: prmKey != null
* Postcondition: Program exits.
*/
void mKey(unsigned char prmKey, int prmX, int prmY)
{
if (prmKey == 'q') exit(0);
}
/**
* mDrawFillCircle()
* Precondition: None
* Postcondition: Draws a filled circle.
*/
void mDrawFillCircle(GLfloat prmX, GLfloat prmY, GLfloat prmRadius)
{
// Surface point
GLfloat vectorSurfX1 = prmX + prmRadius;
GLfloat vectorSurfY1 = prmY;
// Previous surface point
GLfloat vectorSurfX2;
GLfloat vectorSurfY2;
// Start point angle
GLfloat angle;
// Constants
const GLfloat ANGLESTEP = 0.23;
const GLfloat PI = 3.14159;
const GLfloat ANGLETOTAL = 2.0 * PI + ANGLESTEP;
glBegin(GL_TRIANGLES);
for (angle = ANGLESTEP; angle < ANGLETOTAL; angle += ANGLESTEP)
{
// Save previous position
vectorSurfX2 = vectorSurfX1;
vectorSurfY2 = vectorSurfY1;
// Calculate next point
vectorSurfX1 = prmX + prmRadius * cos(angle);
vectorSurfY1 = prmY + prmRadius * sin(angle);
// Plot points
glVertex2d(prmX, prmY);
glVertex2d(vectorSurfX2, vectorSurfY2);
glVertex2d(vectorSurfX1, vectorSurfY1);
}
glEnd();
glFlush();
}
/**
* mDrawCylinder()
* Precondition: None
* Postcondition: Draws cylinder sides.
*/
void mDrawCylinder(GLfloat prmArc,
GLfloat prmRadius,
GLfloat prmHeight,
GLint prmSlices,
GLint prmStacks)
{
// Loop variables
GLint stackCnt;
GLint sliceCnt;
// Angle/Height
GLfloat angle;
GLfloat height;
// Constants
const GLfloat stackHeight = prmHeight/prmStacks;
const GLfloat PI = 3.14159;
const GLfloat radian = PI/180;
for (stackCnt = 0; stackCnt < prmStacks; stackCnt++)
{
height = prmHeight * stackCnt / prmStacks;
glBegin(GL_QUAD_STRIP);
for (sliceCnt = 0; sliceCnt <= prmSlices; sliceCnt++)
{
angle = prmArc * sliceCnt/prmSlices * radian;
glNormal3f(cos(angle), sin(angle), 0.0);
glVertex3d(cos(angle) * prmRadius,
sin(angle) * prmRadius,
height);
glVertex3d(cos(angle) * prmRadius,
sin(angle) * prmRadius,
height + prmHeight);
}
glEnd();
glFlush();
}
}