I'm teaching myself how to use OpenGL to create graphics, and I've got a basic spiral script+rotation. The Y-Axis rotation is automatic based on a timer function, but I noticed that when I move my mouse inside the window, it seems to rotate faster than intended. Could someone please look over my script and tell me what is causing the acceleration of the timer function?
#include <Windows.h>
#include <glut.h>
#include <stdio.h>
#include <math.h>
// Change viewing volume and viewport. Called when window is resized
void ChangeSize(GLsizei w, GLsizei h)
{
GLfloat nRange = 100.0f;
//Prevent a divide by zero
if(h == 0)
h = 1;
// Set Viewport to window dimensions
glViewport(0, 0, w, h);
// Reset projection matrix stack
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Establish clipping volume (left, right, buttom, top, near, far)
if (w<= h)
glOrtho (-nRange, nRange, -nRange*h/w, nRange*h/w, -nRange, nRange);
else
glOrtho (-nRange*w/h, nRange*w/h, -nRange, nRange, -nRange, nRange);
//Reset Model view matrix stack
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
//Define a constant for pi
#define GL_PI 3.1415f
// This function does all the initialization
void SetupRC()
{
// Black background
glClearColor(0.0f, 0.0f, 0.0f, 1.0f );
// Set drawing color to green
glColor3f(0.0f, 1.0f, 0.0f);
}
// Test declaration of rotation angle
GLfloat xRot = 0;
GLfloat yRot = 0;
// Modifiable variables for the eccentricity
GLfloat xMod = 50.0f;
GLfloat yMod = 50.0f;
// Called to draw scene
void RenderScene(void)
{
GLfloat x,y,z,angle; // Storage for coordinates and angles
GLfloat sizes[2]; // Store supported point size range
GLfloat step; // Store point size increments
GLfloat curSize; //Store current point size
// Get supported point size range and step size
glGetFloatv(GL_POINT_SIZE_RANGE, sizes);
glGetFloatv(GL_POINT_SIZE_GRANULARITY, &step);
//Set the initial point size
curSize = sizes[0];
// Clear the window with current clearing color
glClear(GL_COLOR_BUFFER_BIT);
// Save matrix state and do the rotation
glPushMatrix();
glRotatef(xRot, 1.0f, 0.0f, 0.0f);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
// specify point size before primitive is specified
glPointSize(curSize);
//Call only once for remaining points
glBegin(GL_LINE_STRIP);
//Set beginning z coordinate
z = -50.0f;
//Loop around in a circle three times
for (angle = 0.0f; angle <= (2.0f*GL_PI)*3.0f; angle += 0.1f)
{
// Calculate x and y values on the circle (the major and minor axis)
x = xMod*sin(angle);
y = yMod*cos(angle);
// Specify the point and move the z value up a little
glVertex3f(x, y, z);
z += 0.5f;
}
// Done drawing points
glEnd();
// Restore transformations
glPopMatrix();
//Flush drawing commands
glFlush();
}
// Modifier Code
void CircleController (int key, int x, int y)
{
switch (key)
{
case 27 : break;
case 100 :
(yRot -= 5.0f); ; break;
case 102 :
(yRot += 5.0f); ; break;
case 101 :
(xRot -= 5.0f); ; break;
case 103 :
(xRot += 5.0f); ; break;
glutDisplayFunc(RenderScene);
}
}
void MouseHandler (int button, int state, int x, int y)
{
// Holder variable assigned to overcome printf limitation and prevent double- printing due to MouseUp function call
GLfloat Holder = xMod;
// Increases size, and decreases timer speed by increasing the amount of time needed.
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
{
xMod+= 5.0f;
}
// Decreases size, and increases timer speed by decreasing the amount of time needed.
if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN)
{
xMod-= 5.0f ;
}
if (Holder != xMod)
printf("%d \n", Holder);
}
void TimerFunction(int value)
{
//Call the arrow key function
glutSpecialFunc(CircleController);
//Call the Mouseclick Modifier function
glutMouseFunc(MouseHandler);
if (xRot < 360)
(xRot += 1.0f);
else
(xRot = 0.0f);
// Redraw the scene with new coordinates
glutPostRedisplay();
glutTimerFunc(1.6666f, TimerFunction, 1);
}
void main(void)
{
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutCreateWindow("Drawing Lines");
glutDisplayFunc(RenderScene);
glutReshapeFunc(ChangeSize);
glutTimerFunc(1.6666f, TimerFunction, 1);
SetupRC();
glutMainLoop();
}
Eric Palace gave me the start
My personal theory was always that it had something to do with the window focus and which program is given more CPU time, but that's just pure speculation.
That makes sense to me. But wait, don't you only paint on a timer? Wouldn't that prevent additional CPU time from modifying movement speed? Yes you do. Sortof.
glutTimerFunc(1.6666f, TimerFunction, 1);
The doc's for glutTimerFunc say that the first parameter is an unsigned int, representing the timer in milliseconds. So you're telling glut "call this function every 1 millsecond." (Approx 1000FPS) And since it takes longer than one millisecond to execute, you're effectively telling it "run this function over and over as fast as you possibly can". Which it does. And so additional CPU time is making a difference.
To avoid situtations like this (aside from correcting the 1.6666f parameter), it's usually suggested to
update the "world" in separate functions from painting the screen. In fact, I would imagine it to be common to have two or more world update functions. One for stuff that needs updating with the paint: arrows and running characters, one for stuff that only changes once a second or so: mana regen and npc decisions, and one for really slow stuff: respawns.
During an "update", check how much time has passed since the last update, (maxing out at half a second or so), and make the world update that much. Then if updates run twice as often or half as often for any reason, the game doesn't appear to speed up or slow down, you just just more/fewer frames instead.
Here's what such an update might look like
radians xrot = 0; //use a units class
radians rot_per_second = .01;
void updateFrame(double time_passed) {
assert(time_passed>=0 && time_passed <= 1.0);
radians rotate_thistime = rot_per_second * time_passed;
xrot += rotate_thistime;
}
void do_updates() {
static clock_t lastupdatetime = clock()-1; //use openGL functions instead of C
clock_t thisupdatetime = clock();
double seconds = double(thisupdatetime-lastupdatetime)/CLOCKS_PER_SEC;
if (seconds > 0.5) //if something happened and haven't update in a long time
seconds = 0.5; //pretend only half a second passed.
//this can happen if
// computer is overloaded
// computer hibernates
// the process is debugged
// the clock changes
if (seconds <= 0.0) //computer is REALLY fast or clock changed
return; //skip this update, we'll do it when we have sane numbers
updateFrame(seconds);
lastupdatetime = thisupdatetime;
}
Related
I have this code:
/*
* This is a simple program that computes FPS
* by means of a circular buffer
*/
#include <GL/glut.h>
//#include <numeric>
#include <unistd.h>
#include <time.h>
#include <stdio.h>
// Number of elements in the circular buffer
#define NELS 10
// Number of lines
#define NLINES 10000
// circular buffer used to compute frame rate
float circularBuffer[NELS];
int firstInd = 0, nEls = 0;
// function to get the number of elapsed ticks
uint32_t getTick()
{
struct timespec ts;
unsigned theTick = 0U;
clock_gettime( CLOCK_REALTIME, &ts );
theTick = ts.tv_nsec / 1000000;
theTick += ts.tv_sec * 1000;
return theTick;
}
// Function to compute real modulus and NOT remained as % does
inline int modulo(int a, int b) {
const int result = a % b;
return result >= 0 ? result : result + b;
}
// Compute sum of the elements in the circular buffer
float sumCircularBuffer()
{
int ind;
float sum = 0;
if (nEls > 0) {
for (ind=1; ind<=nEls; ind++) {
sum = sum + circularBuffer[modulo(firstInd-ind, NELS)];
}
}
return sum;
}
// accumulate buffer and update window title
void computeAndShowFrameRate(void)
{
static float lastTime = 0.0f;
static unsigned int frameCount = 0;
char windowTitle[100];
float sumFPS;
float currentTime = (float)getTick() * 0.001f;
// Initialize lastTime to the current time
if (lastTime == 0) {
lastTime = currentTime;
}
// increase frame count
frameCount++;
if (currentTime - lastTime > 1.0f) {
// insert the current fps in the circular buffer
circularBuffer[firstInd] = ((float)frameCount) / (currentTime - lastTime);
// update variable lastTime
lastTime = currentTime;
//circularBuffer[firstInd] = (float)frameCount;
firstInd = ((firstInd+1)%NELS);
if (nEls < NELS) {
nEls++;
}
frameCount = 0;
// sum elements in circular buffer
sumFPS = sumCircularBuffer();
snprintf(windowTitle, 100, "FPS = %6.2f", sumFPS/nEls);
// update window title
glutSetWindowTitle(windowTitle);
}
}
// display function
void display(void)
{
int currLineInd;
// get current frame rate
computeAndShowFrameRate();
// clear buffer
glClear (GL_COLOR_BUFFER_BIT);
for (currLineInd = 0; currLineInd<NLINES; currLineInd++) {
// draw line
glBegin(GL_LINES);
// random color
glColor3f((float)rand()/RAND_MAX, (float)rand()/RAND_MAX, (float)rand()/RAND_MAX);
// random first point
glVertex2f((float)rand()/RAND_MAX, (float)rand()/RAND_MAX);
// random color
glColor3f((float)rand()/RAND_MAX, (float)rand()/RAND_MAX, (float)rand()/RAND_MAX);
// random second point
glVertex2f((float)rand()/RAND_MAX, (float)rand()/RAND_MAX);
glEnd();
}
glFinish();
glutPostRedisplay();
}
// initialization function
void init (void)
{
// Use current time as seed for random generator
srand(time(0));
// select clearing color
glClearColor (0.0, 0.0, 0.0, 0.0);
// Orthographic projection
glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0);
}
// Window size and mode
int main(int argc, char** argv)
{
// pass potential input arguments to glutInit
glutInit(&argc, argv);
// set display mode
// GLUT_SINGLE = single buffer window
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize (400, 400);
glutInitWindowPosition (100, 100);
glutCreateWindow ("OpenGL Window");
// Call initialization routinesx
init();
glutDisplayFunc(display);
glutMainLoop();
return 0;
}
I have to replace the glOrtho function with glFrustum and get the same result.
I read the opengl guide on khronos and understood the differences between glOrtho and glFrustum but i can't figure out how to calculate the parameters.
How do I calculate the parameters to pass to the glFrustum function?
glFrustum() require 6 parameters to specify 6 clipping planes: left, right, bottom, top, near and far planes. A visual representation would be like this:
The values that will come up with depend on your implementation and the scale of the models you are working with. As mentioned above, if the projected geometry is in front of the near plane or behind the far plane it will be clipped thus it won't be visible.
To solve this you either have to recompute the parameters for the glFrustum() function(bad idea) or move the camera/scene along the z-axis.
References:
http://www.songho.ca/opengl/gl_transform.html
https://learnopengl.com/Getting-started/Coordinate-Systems
With Perspective projection the distnace to the near and far plane have to be grater than 0,
0 < near < far
because you want to define a Viewing frustum:
If the distance to the near plane is less than 0, the result is undefined (Usually the instruction has no effect at all).
See glFrustum:
void glFrustum( GLdouble left,
GLdouble right,
GLdouble bottom,
GLdouble top,
GLdouble nearVal,
GLdouble farVal);
The distances left, right, bottom and top, are the distances from the center of the view to the side faces of the frustum, on the near plane. near and far specify the distances to the near and far plane of the frustum.
The geometry has to be between the near and the far plane, else it is clipped. Therefore, you have to move the shift along the z-axis in the negative direction (in negativ direction, because the view space z-axis points out of the view):
// initialization function
void init (void)
{
// [...]
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-0.1, 0.1, -0.1, 0.1, 0.1, 50.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0f, 0.0f, -5.0f);
}
I've put together a 3rd person camera system using OpenGL and C++ from tutorials and such online, but I can't seem to figure out a specific problem. When I turn using mouse movement, my character is rotated around the camera rather than the camera around the character and the character turning on the spot. What should I do to have the character turn on the spot?
// variables ..
void checkMouse(){
if (mouseXPos > SCREEN_WIDTH/2){
// turn right
yrot += abs(mouseXPos - SCREEN_WIDTH/2) * .005;
} else if (mouseXPos < SCREEN_WIDTH/2){
// turn left
yrot -= abs(mouseXPos - SCREEN_WIDTH/2) * .005;
}
if (mouseYPos > SCREEN_HEIGHT/2){
// look up
xrot += abs(mouseYPos - SCREEN_HEIGHT/2) * .005;
} else if (mouseYPos < SCREEN_HEIGHT/2){
// look down
xrot -= abs(mouseYPos - SCREEN_HEIGHT/2) * .005;
}
}
void checkKeys(){
if(keys['t'] == true){
wireframe=!wireframe;
if(wireframe){
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
else glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
if (keys['w'] == true){
float xrotrad, yrotrad;
yrotrad = (yrot / 180 * 3.141592654f);
xrotrad = (xrot / 180 * 3.141592654f);
xpos += float(sin(yrotrad)) * 10 ;
zpos -= float(cos(yrotrad)) * 10 ;
}
if (keys['s'] == true){
float xrotrad, yrotrad;
yrotrad = (yrot / 180 * 3.141592654f);
xrotrad = (xrot / 180 * 3.141592654f);
xpos -= float(sin(yrotrad)) * 10;
zpos += float(cos(yrotrad)) * 10;
}
if (keys['a'] == true){
float yrotrad;
yrotrad = (yrot / 180 * 3.141592654f);
xpos -= float(cos(yrotrad)) * 10;
zpos -= float(sin(yrotrad)) * 10;
}
if (keys['d'] == true){
float yrotrad;
yrotrad = (yrot / 180 * 3.141592654f);
xpos += float(cos(yrotrad)) * 10;
zpos += float(sin(yrotrad)) * 10;
}
}
void renderScene(){
// Clear framebuffer & depth buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Reset Modelview matrix
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Set view position & direction
gluLookAt(0,0,5, 0,0,-1, 0,1,0);
checkKeys();
checkMouse();
// 3rd person object
// draw body
glPushMatrix();
glRotatef(xrot,1.0,0.0,0.0); // keeps object on ground level rather than always in front of camera
glTranslatef(0,-90,-400.0); // keep object 400 away from camera
glRotatef(-90,0.0,1.0,0.0);
glutSolidCube(20);
glPopMatrix();
// CAMERA
glRotatef(xrot,1.0,0.0,0.0); //rotate our camera on the x-axis (left and right)
glRotatef(yrot,0.0,1.0,0.0); //rotate our camera on the y-axis (up and down)
glTranslated(-xpos,-ypos-200,-zpos);
// rest of world
glPushMatrix();
glutSolidCube(30);
glPopMatrix();
// ..
glDisable(GL_TEXTURE_2D);
// Swap double buffer for flicker-free animation
glutSwapBuffers();
}
void updateScene(){
// Wait until at least 16ms passed since start of last frame
// Effectively caps framerate at ~60fps
while(timeGetTime()-lastTickCount<16);
lastTickCount=timeGetTime();
// Draw the next frame
glutPostRedisplay();
}
void keypress (unsigned char key, int x, int y) {
keys[key] = true;
// Test if user pressed ESCAPE (ascii 27)
// If so, exit the program
if(key==27){
exitScene();
}
}
void keypressup (unsigned char key, int x, int y) {
keys[key] = false;
wheel_turn = 0;
}
void mouseMovement(int x, int y) {
mouseXPos = x;
mouseYPos = y;
}
void mouseClick(int button, int state, int x, int y){
if (button == GLUT_LEFT_BUTTON){
if (state == GLUT_DOWN)
lButton = true;
else
lButton = false;
}
}
void setupScene(){
forwards = 0;
strafe = 0;
turn = 0;
std::cout<<"Initializing scene..."<<std::endl;
//Set up Lighting Stuff
glLightfv(GL_LIGHT0, GL_POSITION, left_light_position);
glLightfv(GL_LIGHT0, GL_AMBIENT, white_light);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glLightfv(GL_LIGHT0, GL_DIFFUSE, white_light);
glShadeModel(GL_SMOOTH);
glEnable(GL_DEPTH_TEST);
}
void exitScene(){
std::cout<<"Exiting scene..."<<std::endl;
// Close window
glutDestroyWindow(windowId);
// Free any allocated memory
// Exit program
exit(0);
}
void setViewport(int width, int height) {
// Work out window ratio, avoid divide-by-zero
if(height==0)height=1;
float ratio = float(width)/float(height);
// Reset projection matrix
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Fill screen with viewport
glViewport(0, 0, width, height);
// Set a 45 degree perspective
gluPerspective(45, ratio, .1, 200000);
}
int main(int argc, char *argv[]){
// Initialise OpenGL
glutInit(&argc, argv);
// Set window position, size & create window
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowPosition(50,50);
glutInitWindowSize(SCREEN_WIDTH,SCREEN_HEIGHT);
windowId = glutCreateWindow("3rd person cam");
// Set GLUT callback functions
glutReshapeFunc(setViewport);
glutDisplayFunc(renderScene);
glutIdleFunc(updateScene);
glutKeyboardFunc(keypress);
glutKeyboardUpFunc(keypressup);
glutPassiveMotionFunc(mouseMovement); //check for mouse movement
glutMotionFunc(mouseMovement);
glutMouseFunc(mouseClick);
// Setup OpenGL state & scene resources (models, textures etc)
setupScene();
// Show window & start update loop
glutMainLoop();
return 0;
}
You're rotating the camera around itself — it's akin to you turning your head. You want to change the camera position, revolving around your object of interest.
1. Find your camera position
Look up 'spherical coordinates' for this
Your horizontal angle should vary between (0 and 2*PI) based on mouse x move
Your vertical angle should vary between (0 and PI) based on mouse y move
You can scale the found (x,y,z) position with a value to vary the distance between camera and object
Add the object position to this found position
You now have a valid camera position around your object
2. Find View matrix
There's a handy glut method called gluLookAt, just use that to find your final camera matrix. It needs (camera positoin, object position, and world up(0,1,0))
I am trying to move a pyramid around in opengl in C++ and I am trying to get it to move forward in the direction it is facing. However, I cannot seem to figure out how to do this. This is my code for drawing my pyramid:
void drawTriangle()
{
//back
glBegin(GL_POLYGON);
glColor3f(1,0,0);
glVertex3f(-1,-1,-1);
glVertex3f(1,-1,-1);
glVertex3f(0,1,0);
glEnd();
//front
glBegin(GL_POLYGON);
glColor3f(0,1,0);
glVertex3f(-1,-1,1);
glVertex3f(1,-1,1);
glVertex3f(0,1,0);
glEnd();
//right
glBegin(GL_POLYGON);
glColor3f(0,0,1);
glVertex3f(1,-1,-1);
glVertex3f(1,-1,1);
glVertex3f(0,1,0);
glEnd();
//left
glBegin(GL_POLYGON);
glColor3f(1,1,0);
glVertex3f(-1,-1,-1);
glVertex3f(-1,-1,1);
glVertex3f(0,1,0);
glEnd();
//bottom
glBegin(GL_POLYGON);
glColor3f(1,0,1);
glVertex3f(-1,-1,-1);
glVertex3f(1,-1,-1);
glVertex3f(1,-1,1);
glVertex3f(-1,-1,1);
glEnd();
}
This is how the pyramid is drawn to the screen:
glPushMatrix();
glTranslatef(Xtri,0,Ztri);
glRotatef(heading,0,1,0);
drawTriangle();
glPopMatrix();
glFlush();
And here is how the variables are updated so that the pyramid could move around :
void processKeys()
{
if(keys[VK_LEFT])
{
heading-=1.0f;
}
if(keys[VK_RIGHT])
{
heading+=1.0f;
}
if(keys[VK_UP])
{
Vtri-=0.001f;
}
if(keys[VK_DOWN])
{
Vtri+=0.001f;
}
}
void update()
{
Xtri += Vtri*cos((90+heading)*(PI/180.0f));
Ztri += Vtri*sin((90+heading)*(PI/180.0f));
}
I am trying to get the pyramid to move forward so that the red or back face is the face that I want to be the direction that the pyramid moves in but when I use this code it doesn't work that way at all and it moves in a very funny way.
Here's one way you can do it. It's based on Direct3D, but I assume it should be very similar for OpenGL.
Have two vectors for your pyramid: one containing its position and another one containing its rotation. Initially both initialized to 0,0,0. Also have a direction vector initially facing in the direction your object does (e.g. 1,0,0) and speed, again set to 0.
Upon pressing a or d adjust the y element of the rotation vector to a desired angle. Similarly upon pressing w or s adjust the speed of your object. You probably want to take frame rendering time into account to make the movement fps independent.
Then create a rotation matrix around Y axis, like you do. Use that matrix to create new normalized vector by multiplying it with the initial position vector. Then add this vector multiplied by speed to your position coordinates and use these coordinates for translation matrix.
Hope it makes sense. Here's some pseudo-code:
// in constructor:
x = y = z = 0.0f; // position
ry = 0.0f; // rotation
speed = 0.0f;
initVec(1,0,0); // initial direction vector
// in event handler:
// upon key press adjust speed and/or ry
// in move():
// create rotation matrix around Y axis by ry = rotationMatrix
// use it to find new direction vector
newDirectionVector = initVec * rotationMatrix;
// adjust your position
x += newDirectionVector * speed;
y += newDirectionVector * speed;
z += newDirectionVector * speed;
glTranslatef(x, y, z);
// render
I came across this when searching for ideas for a similar problem, and decided to share my solution:
For a triangle with 1 unit base, and 2 units height, on y = 0.3 = pos[2]
void triangle::draw() {
glPushMatrix();
//pos[0] is the x value initialized to 0
//pos[2] is the z value initialized to 0
glTranslatef(pos[0], 0, pos[2]);
glRotatef(direction, 0, 1, 0);
glBegin(GL_TRIANGLES);
glVertex3f(.5, pos[1], 0);
glVertex3f(-.5, pos[1], 0);
glVertex3f(0, pos[1], 2);
glEnd();
glPopMatrix();
}
//called when key w is pressed
void triangle::forward() {
// convert degrees to rads and multiply (I used 0.5)
pos[0] += sin(M_PI * direction / 180) * .5;
pos[2] += cos(M_PI * direction / 180) * .5;
std::cout << pos[0] << "," << pos[2] << std::endl;
}
//called when key s is pressed
void triangle::back() {
pos[0] -= sin(M_PI * direction / 180) * .5;
pos[2] -= cos(M_PI * direction / 180) * .5;
std::cout << pos[0] << "," << pos[2] << std::endl;
}
//called when key d is pressed
void triangle::right() {
direction -= 5;
//direction is the angle (int)
//this is probably not needed but, if you keep turning in the same direction
//an overflow is not going to happen
if (direction <= 360)direction %= 360;
std::cout << direction << std::endl;
}
//called when key a is pressed
void triangle::left() {
direction += 5;
if (direction >= -360)direction %= 360;
std::cout << direction << std::endl;
}
Hope I was of help, for someone facing similar problems bumping into this.
You need to do rotations before translations, assuming your object is at the origin to begin with (if I recall my math correctly). I believe the rotation matrix applied rotates around the axis (0, 1, 0), if you've already translated to a new location, that rotation will affect your position as well as direction you are facing.
Alright, I'm trying to recreate the old classic, Missile Command, using OpenGL in C++. This is my first foray into OpenGL, although I feel fairly comfortable with C++ at this point.
I figured my first task was to figure out how to move 2d objects around the screen, seemed like it would be fairly simple. I created two quick method calls to make either triangles or quads:
void makeTriangle(color3f theColor, vertex2f &p1, vertex2f &p2, vertex2f &p3,
int &xOffset, int &yOffset)
{
//a triangle
glBegin(GL_POLYGON);
glColor3f(theColor.red, theColor.green, theColor.blue);
glVertex2f(p1.x, p1.y);
glVertex2f(p2.x, p2.y);
glVertex2f(p3.x, p3.y);
glEnd();
}
void makeQuad(color3f theColor, vertex2f &p1, vertex2f &p2, vertex2f &p3,
vertex2f &p4, int &xOffset, int &yOffset)
{
//a rectangle
glBegin(GL_POLYGON);
glColor3f(theColor.red, theColor.green, theColor.blue);
glVertex2f(p1.x, p1.y);
glVertex2f(p2.x, p2.y);
glVertex2f(p3.x, p3.y);
glVertex2f(p4.x, p4.y);
glEnd();
}
color3f and vertex2f are simple classes:
class vertex2f
{
public:
float x, y;
vertex2f(float a, float b){x=a; y=b;}
};
class color3f
{
public:
float red, green, blue;
color3f(float a, float b, float c){red=a; green=b; blue=c;}
};
And here is my main file:
#include <iostream>
#include "Shapes.hpp"
using namespace std;
int xOffset = 0, yOffset = 0;
bool done = false;
void keyboard(unsigned char key, int x, int y)
{
if( key == 'q' || key == 'Q')
{
exit(0);
done = true;
}
if( key == 'a' )
xOffset = -10;
if( key == 'd' )
xOffset = 10;
if( key == 's' )
yOffset = -10;
if( key == 'w' )
yOffset = 10;
}
void init(void)
{
//Set color of display window to white
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
//Set parameters for world-coordiante clipping window
glMatrixMode(GL_PROJECTION);
gluOrtho2D(-400.0,400.0,-300.0,300.0);
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
color3f aGreen(0.0, 1.0, 0.0);
vertex2f pa(-400,-200);
vertex2f pb(-400,-300);
vertex2f pc(400,-300);
vertex2f pd(400,-200);
makeQuad(aGreen,pa,pb,pc,pd,xOffset,yOffset);
color3f aRed(1.0, 0.0, 0.0);
vertex2f p1(-50.0,-25.0);
vertex2f p2(50.0,-25.0);
vertex2f p3(0.0,50.0);
makeTriangle(aRed,p1,p2,p3,xOffset,yOffset);
glFlush();
}
int main(int argc, char** argv)
{
// Create Window.
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT);
glutCreateWindow("test");
// Some initialization.
init();
while(!done)
{
//display functions
glutDisplayFunc(display);
glutKeyboardFunc(keyboard);
// Start event loop.
glutMainLoop();
}
return 0;
}
A quad is defined as the "background" for the time being and consists of just a green rectangle along the bottom of the screen. The red triangle is the "object" that I wish to move. On a keypress, an offset is saved in the direction indicated.
I've tried using glTranslatef(xOffset,yOffset,0); but the problem with that is that it moves both elements on the screen and not just the red triangle. I attempted to put the whole call to draw the triangle between a push and pop matrix operation:
PushMatrix();
glTranslatef(xOffset,yOffset,0);
glBegin(GL_POLYGON);
glColor3f(theColor.red, theColor.green, theColor.blue);
glVertex2f(p1.x, p1.y);
glVertex2f(p2.x, p2.y);
glVertex2f(p3.x, p3.y);
glEnd();
PopMatrix();
As far as I can tell, that destroys any changes that the translation was doing beforehand.
I've also tried just changing the values of the x and y coordinates before calling the draw, but that just causes a brief flicker before leaving the triangle in its original position:
p1.x += xOffset;
p2.x += xOffset;
p3.x += xOffset;
p1.y += yOffset;
p2.y += yOffset;
p3.y += yOffset;
There has to be a nice simple way of doing this, and I'm just overlooking it. Could someone offer a suggestion please?
EDIT:
My actual problem was that I was never refreshing the screen after an initial draw. What I needed was to specify an idle function inside my main loop:
glutIdleFunc(IdleFunc);
Where the actual IdleFunc looks like:
GLvoid IdleFunc(GLvoid)
{
glutPostRedisplay();
}
Instead of using glFlush() inside my draw function, I should have been using glutSwapBuffers(). By doing that, the code I had first come up with:
p1.x += xOffset;
p2.x += xOffset;
p3.x += xOffset;
p1.y += yOffset;
p2.y += yOffset;
p3.y += yOffset;
Works fine for my purposes. I didn't have a need to translate the matrix, I just needed to draw the element in a different position from one scene to the next.
GL_MODELVIEW is what you need.
From the OpenGL FAQ, 2.1: http://www.opengl.org/resources/faq/technical/gettingstarted.htm
program_entrypoint
{
// Determine which depth or pixel format should be used.
// Create a window with the desired format.
// Create a rendering context and make it current with the window.
// Set up initial OpenGL state.
// Set up callback routines for window resize and window refresh.
}
handle_resize
{
glViewport(...);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Set projection transform with glOrtho, glFrustum, gluOrtho2D, gluPerspective, etc.
}
handle_refresh
{
glClear(...);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// Set view transform with gluLookAt or equivalent
// For each object (i) in the scene that needs to be rendered:
// Push relevant stacks, e.g., glPushMatrix, glPushAttrib.
// Set OpenGL state specific to object (i).
// Set model transform for object (i) using glTranslatef, glScalef, glRotatef, and/or equivalent.
// Issue rendering commands for object (i).
// Pop relevant stacks, (e.g., glPopMatrix, glPopAttrib.)
// End for loop.
// Swap buffers.
}
You answer your own question, that is the solution:
glPushMatrix();
glTranslatef(xOffset,yOffset,0);
glBegin(GL_POLYGON);
glColor3f(theColor.red, theColor.green, theColor.blue);
glVertex2f(p1.x, p1.y);
glVertex2f(p2.x, p2.y);
glVertex2f(p3.x, p3.y);
glEnd();
glPopMatrix();
That will change the modelview matrix while the rectangle is drawn, then it will revert the modelview matrix back to what it were before. Did you actualy tried that? What whent wrong?
If I'm reading your code right, you want to only rotate one element right? If so, do this:
Call glPushMatrix();
then do your rotation
Store how much you've rotated
then draw your rotated item
then call glPopMatrix();
That will only rotate the one object.
EDIT:
I see that doing that "destroys" the previous rotation. Could you elaborate? That is the correct way to translate/rotate one object.
I also notice that you aren't initializing the Modelview Matrix. You should initialize the Modelview Matrix after you setup your PROJECTION matrix. You also need to make sure that you are initializing both matrices to the identity. And finally, make sure that you are initializing both matrices EVERY time the screen refreshes. To test this, set a breakpoint on your matrix initialization and see if it gets hit only once or every frame.
I have been following this tutorial series for OpenGL: GLUT:
http://www.lighthouse3d.com/opengl/glut/
I have reached the stage of implementing camera controls using the keyboard:
http://www.lighthouse3d.com/opengl/glut/index.php?8
When doing the advanced tutorial it stops working. I've pretty much just copied and pasted it. When I run its version of the code it works. Mine just doesn't seem to work. It should rotate the camera view when moving left and right and move forward and backwards when using up and down keys.
My code is here broken down:
This part of my code renders components in the scene with init() which initilizes values etc.:
void display(void)
{
if (deltaMove)
moveMeFlat(deltaMove);
if (deltaAngle) {
angle += deltaAngle;
orientMe(angle);
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindTexture(GL_TEXTURE_2D, texture[0]);
RenderSkyDome();
glBindTexture(GL_TEXTURE_2D, NULL);
glutSwapBuffers();
}
void init(void)
{
glClearColor(0.0, 0.0, 0.0, 0.0);
glClearDepth(1.0f);
glColor3f(0.0, 0.0, 1.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glEnable(GL_TEXTURE_2D);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
LoadTextures("clouds2.bmp", 0);
GenerateDome(600.0f, 5.0f, 5.0f, 1.0f, 1.0f);
snowman_display_list = createDL();
}
This is my main loop function:
int main ()
{
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowSize(800, 600);
glutInitWindowPosition(0, 0);
glutCreateWindow("Captain Ed's Adventures: Great Wall of China");
init();
//Glut Input Commands
glutIgnoreKeyRepeat(1);
glutSpecialFunc(pressKey);
glutSpecialUpFunc(releaseKey);
glutKeyboardFunc(processNormalKeys);
glutDisplayFunc(display);
glutIdleFunc(display);
glutReshapeFunc(reshape); // This redraws everything on screen when window size is changed.
glutMainLoop();
return 0;
}
Here are my input functions which are called:
void pressKey(int key, int x, int y) {
switch (key) {
case GLUT_KEY_LEFT : deltaAngle = -0.10f;break;
case GLUT_KEY_RIGHT : deltaAngle = 0.10f;break;
case GLUT_KEY_UP : deltaMove = 50;break;
case GLUT_KEY_DOWN : deltaMove = -50;break;
}
}
void releaseKey(int key, int x, int y) {
switch (key) {
case GLUT_KEY_LEFT : if (deltaAngle < 0.0f)
deltaAngle = 0.0f;
break;
case GLUT_KEY_RIGHT : if (deltaAngle > 0.0f)
deltaAngle = 0.0f;
break;
case GLUT_KEY_UP : if (deltaMove > 0)
deltaMove = 0;
break;
case GLUT_KEY_DOWN : if (deltaMove < 0)
deltaMove = 0;
break;
}
}
void processNormalKeys(unsigned char key, int x, int y) {
if (key == 27)
exit(0);
}
Variables that get used by the camera and the functions that should change it:
static float angle=0.0,deltaAngle = 0.0,ratio;
static float x=0.0f,y=1.75f,z=5.0f;
static float lx=0.0f,ly=0.0f,lz=-1.0f;
static int deltaMove=0;
void orientMe(float ang) {
lx = sin(ang);
lz = -cos(ang);
glLoadIdentity();
gluLookAt(x, y, z,
x + lx,y + ly,z + lz,
0.0f,1.0f,0.0f);
}
void moveMeFlat(int i) {
x = x + i*(lx)*0.1;
z = z + i*(lz)*0.1;
glLoadIdentity();
gluLookAt(x, y, z,
x + lx,y + ly,z + lz,
0.0f,1.0f,0.0f);
}
I believe that's pretty much everything I am working with so basically what should happen is that when I press UP key deltaMove should = 50 and when this happens the if statement in void display(void) should do moveMeFlat(deltaMove); I don't know if I am doing this wrong or if there is a better result....
I can move "moveMeFlat(deltaMove)" within the relevant switch cases but this does not allow me to have the movement that I want. It seems to work using the source code from the tutorials above with the right functionality but not in my case so my assumption is that it's to do with the if statement in display.
The end result I am looking for is to be able to have forwards and backwards working with left and right rotating the camera. I would like to be able to press forward and left key and see the camera swerve left like in a racing game...
Try getting rid of static in front of all of your global variable declarations.
The last use of static is as a global variable inside a file of code. In this case, the use of static indicates that source code in other files that are part of the project cannot access the variable. Only code inside the single file can see the variable. (It's scope -- or visibility -- is limited to the file.) This technique can be used to simulate object oriented code because it limits visibility of a variable and thus helps avoid naming conflicts. This use of static is a holdover from C.
-http://www.cprogramming.com/tutorial/statickeyword.html
Also, instead of this:
if (deltaMove)
moveMeFlat(deltaMove);
if (deltaAngle) {
angle += deltaAngle;
orientMe(angle);
}
try this:
if (deltaMove != 0)
moveMeFlat(deltaMove);
if (deltaAngle != 0.0) {
angle += deltaAngle;
orientMe(angle);
}
Is GLUT idle when a key press is occurring? I think it is not idle. Your display() function is only called when the window is created and when GLUT is idle, which doesn't allow your position to be changed.
I suggest adding:
glutTimerFunc(40, display, 1);
to the end of your display function. This will cause display to be called every 40 milliseconds.
Also, don't register display as the idle function, or you'll have the idle function starting lots of series of timer functions.
P.S. I would call glFlush(); just before swapping the buffers in your display function.