C++/Opengl C++ in Linux - Animation/evolve not working
Expert: Ralph McArdell - 11/28/2007
QuestionHi Ralph, I am very new to C++/opengl and I have been having alot of trouble with my C++ code for the Linux OS. What i need to do is Produce the basic animation for a golf shot, whereby the user defines the angles and magnitude for the shot. The class golf is defined in a seperate file (golf.c++) and then run in golftest.c++ . My problem is that the actual animation part is not working, i print out the velocities and XYZ and i can see that in theory the golf ball is moving but the animation of the shape (sphere) remains stationary at (0,0,0). Any help you could give on solving this would be greatly appreciated. Also how could i go about adding a 3D camera to the scene?
Code for golf.c++
#include <iostream.h>
#include <math.h>
#include <stdlib.h>
float myrand(){return((1.0*rand())/RAND_MAX);}
class golf{ //define the object/class
public:
float getx();float gety();float getz();
float getvx();float getvy();float getvz();
golf(float ,float, float);
void evolve(float t);
bool inplay();
static void movepin();
private:float x,y,z,vx,vy,vz,time;
static float g,pinx,pinz,c_o_r;
};
//Class Variables
float golf::g=0.1;
float golf::pinx=0.5;
float golf::pinz=0.5;
float golf::c_o_r=0.5;
float golf::getx(){ return x; }
float golf::gety(){ return y; }
float golf::getz(){ return z; }
float golf::getvx(){ return vx; }
float golf::getvy(){ return vy; }
float golf::getvz(){ return vz; }
float pi = 3.14259;
void golf::movepin()
{
pinx = myrand();
pinz = myrand();
}
golf::golf(float anglex, float anglez, float mag )
{
x=0,y=0,z=0, time=0;
// given magnitude and anglex and angle y
// assign to vx , vy and vz
vx = mag*cos(anglez*(pi/180))*cos(anglex*(pi/180));
vy = mag*cos(anglez*(pi/180))*sin(anglex*(pi/180));
vz = mag*cos((3.14/2)-anglez*(pi/180));
}
bool golf::inplay()
{
if(x>1 || x<-1) return false;
if(y>1 || y<-1) return false;
if(z>1 || z<-1) return false;
// etc
}
void golf::evolve(float delta){
time+=delta;
y+=vy*delta;
x+=vx*delta;
z+=vz*delta;
//Print out xyz and velocities
cout << "x= " << x << " y= " << y << " z = " << z << "\n";
cout << "vx= " << vx << "vy= " << vy << " vz = " << vz << "\n";
vz+=-g*delta;
if(z<0)
{
vz=-1*c_o_r; // bounce
z=.001; // make sure ball is above ground
}
inplay();
}
code for golftest.c++
#include "golf.c++"
#include <iostream.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <GL/glut.h>
void init(void){}
//Global variables
float angle1=45; //set default values for angle and mag for testing purposes
float angle2=45;
float mag=5;
char c='#';
float delta=0.01;
//initiate gball
golf gball = golf(0,0,0);
void keyboard(unsigned char key, int x, int y)
{
if(key!='f')
{
cout << "processing " << key << "\n" ; //check that key press has registered
switch(key)
{
case('s'):
angle1+=5; if(angle1>180)angle1=180;
break;
case('a'):
angle1-=5; if(angle1<0)angle1=0;
break;
case('w'):
angle2+=5; if(angle2>90)angle2=90;
break;
case('x'):
angle2-=5; if(angle2<0)angle2=0;
break;
case('o'):
mag+=0.1;
break;
case('p'):
mag+=-0.1; if(mag<0)mag=0;
break;
case('m'):
golf::movepin();
break;
}
//print chages made to angles and magnitude
cout << "angle1 " << angle1 << " angle2 " << angle2 << " magnitude " << mag << "\n";
}
else
{
cout << "fire\n"; //test whether this part of code is reached
golf gball = golf(angle1,angle2,mag);
float time =0;
while(gball.inplay())
{
gball.evolve(delta);
glutPostRedisplay();
}
}
}
void display (void)
{
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
glColor3f (1.0, 1.0, 1.0);
glTranslatef(gball.getx(),gball.gety(),gball.getz());
glutSolidSphere(0.01,10,10);
glFlush();glutSwapBuffers();
}
void anim()
{
glutPostRedisplay();
}
int main(int argc, char * argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize (800, 800);
glutCreateWindow (argv[0]);
init();
glutKeyboardFunc (keyboard);
glutDisplayFunc (display);
glutIdleFunc(anim);
glutMainLoop();
return 0;
}
Thank you very much for any help you can give.
Matt
AnswerI am not an expert in OpenGL but it seems to me from a quick look at your code that you have two versions of an object called gball that are separate objects:
1/ The global gball defined here:
//initiate gball
golf gball = golf(0,0,0);
And accessed in the display function:
// Note: (void) not required for C++ for parameter-less function.
// We can just use ()
void display (void)
{
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
glColor3f (1.0, 1.0, 1.0);
glTranslatef(gball.getx(),gball.gety(),gball.getz());
2/ A local automatic object defined and used for all updates in the keyboard function:
else
{
cout << "fire\n"; //test whether this part of code is reached
golf gball = golf(angle1,angle2,mag); // _NOT_ global gball
This second object is allocated on the stack frame of function calls to keyboard lives only for as long as a function call to the keyboard function, and is only visible to code of the keyboard function. In fact the C++ scope rules mean that it only lives for the duration of (and is visible within) the block in which it is defined, in this case the else block from:
else
{
cout << "fire\n"; //test whether this part of code is reached
golf gball = golf(angle1,angle2,mag); // local gball visible
// from here...
// ...
glutPostRedisplay();
}
// ...to here
} // local gball destroyed here
Thus the gball you are updating in keyboard is not the global one that you are using in display. The global gball is never therefore updated and always has its initial values of 0.0, 0.0, 0.0.
Now the way you are initialising the gball objects is not efficient, you are creating a temporary golf object using the golf(0,0,0) (for global gball), which is then copied to the gball golf object and finally the temporary golf object is destroyed. The more usual (and efficient) way to define and initialise an object is to call the constructor directly on the object name:
golf gball(0,0,0);
The only exception is for default construction which takes no parameters and you may think would be defined like so:
Class_type object(); // ### Does NOT default construct object
But this looks to C++ like a function declaration of a function called object taking no parameters and returning a Class_type object, and in C++ (I think inherited from C) anything that looks like a function declaration is taken to be one - doh! The way around this is to simply not include the parentheses, the (), thus:
Class_type object; // Default constructs object
Now there may be other problems with your code but I do not have time to fully debug other people's code for them. However I hope these few points will help you move forward.