I am fairly new to openGL. One exercise was to rewrite a piece of code using vertex arrays. This is what I come up with. When I compile and then run the .exe all I get is a white window. I think it could be an index variable I have messed up. I think.
#include <cmath>
#include <iostream>
#ifdef __APPLE__
# include <GL/glew.h>
# include <GL/freeglut.h>
# include <OpenGL/glext.h>
#else
# include <GL/glew.h>
# include <GL/freeglut.h>
# include <GL/glext.h>
#pragma comment(lib, "glew32.lib")
#endif
#define PI 3.14159265
using namespace std;
// Globals.
static float R = 5.0; // Radius of hemisphere.
static int p = 6; // Number of longitudinal slices.
static int q = 4; // Number of latitudinal slices.
static float Xangle = 0.0, Yangle = 0.0, Zangle = 0.0; // Angles to rotate hemisphere.
static float *vert;
static int *ind;
// Fill the vertex array with co-ordinates of the sample points.
void fillVerArr(void)
{
int k = 0;
for (int j = 0; j <= q; j++)
{
for (int i = 0; i <= p; i++)
{
vert[k++] = R * sin( (float)j/q * PI/2.0 ) * cos( 2.0 * (float)i/p * PI );
vert[k++] = R * sin( (float)j/q * PI/2.0 ) * sin( 2.0 * (float)i/p * PI );
vert[k++] = R * cos( (float)j/q * PI/2.0 );
}
}
}
// Fill the array of index arrays.
void fillIndArr(int j)
{
for(int i = 0; i <= p; i++)
{
ind[2*i] = (j+1)*p+(i+1);
ind[2*i+1] = j*p + i;
}
}
// Initialization routine.
void setup(void)
{
glClearColor(1.0, 1.0, 1.0, 0.0);
// Enable vertex array.
glEnableClientState(GL_VERTEX_ARRAY);
}
// Drawing routine.
void drawScene(void)
{
int i, j;
glClear (GL_COLOR_BUFFER_BIT);
glLoadIdentity();
// Command to push the hemisphere, which is drawn centered at the origin,
// into the viewing frustum.
glTranslatef(0.0, 0.0, -10.0);
vert = new float[3 * (p+1) * (q+1)];
fillVerArr();
// Commands to turn the hemisphere.
glRotatef(Zangle, 0.0, 0.0, 1.0);
glRotatef(Yangle, 0.0, 1.0, 0.0);
glRotatef(Xangle, 1.0, 0.0, 0.0);
// Hemisphere properties.
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glColor3f(0.0, 0.0, 0.0);
glVertexPointer(3,GL_FLOAT,0,vert);
for(j = 0; j < q; j++)
{
ind = new int[2*p];
fillIndArr(j);
glDrawElements(GL_TRIANGLE_STRIP,2*(p+1) + 1,GL_FLOAT,ind );
}
glFlush();
}
// OpenGL window reshape routine.
void resize(int w, int h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-5.0, 5.0, -5.0, 5.0, 5.0, 100.0);
glMatrixMode(GL_MODELVIEW);
}
// Keyboard input processing routine.
void keyInput(unsigned char key, int x, int y)
{
switch(key)
{
case 27:
exit(0);
break;
case 'P':
p += 1;
glutPostRedisplay();
break;
case 'p':
if (p > 3) p -= 1;
glutPostRedisplay();
break;
case 'Q':
q += 1;
glutPostRedisplay();
break;
case 'q':
if (q > 3) q -= 1;
glutPostRedisplay();
break;
case 'x':
Xangle += 5.0;
if (Xangle > 360.0) Xangle -= 360.0;
glutPostRedisplay();
break;
case 'X':
Xangle -= 5.0;
if (Xangle < 0.0) Xangle += 360.0;
glutPostRedisplay();
break;
case 'y':
Yangle += 5.0;
if (Yangle > 360.0) Yangle -= 360.0;
glutPostRedisplay();
break;
case 'Y':
Yangle -= 5.0;
if (Yangle < 0.0) Yangle += 360.0;
glutPostRedisplay();
break;
case 'z':
Zangle += 5.0;
if (Zangle > 360.0) Zangle -= 360.0;
glutPostRedisplay();
break;
case 'Z':
Zangle -= 5.0;
if (Zangle < 0.0) Zangle += 360.0;
glutPostRedisplay();
break;
default:
break;
}
}
// Routine to output interaction instructions to the C++ window.
void printInteraction(void)
{
cout << "Interaction:" << endl;
cout << "Press P/p to increase/decrease the number of longitudinal slices." << endl
<< "Press Q/q to increase/decrease the number of latitudinal slices." << endl
<< "Press x, X, y, Y, z, Z to turn the hemisphere." << endl;
}
// Main routine.
int main(int argc, char **argv)
{
printInteraction();
glutInit(&argc, argv);
glutInitContextVersion(2, 1);
glutInitContextProfile(GLUT_COMPATIBILITY_PROFILE);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
glutInitWindowSize(500, 500);
glutInitWindowPosition(100, 100);
glutCreateWindow("hemisphere.cpp");
// registers callback routines
glutDisplayFunc(drawScene);
glutReshapeFunc(resize);
glutKeyboardFunc(keyInput);
glewExperimental = GL_TRUE;
glewInit();
// call setup()
setup();
// run the event processing loop, calling callback routines as needed.
glutMainLoop();
}
Your specify the types of the values in the index array as GL_FLOAT in the call to glDrawElements, while they are in fact of type int.
Values of the index array must be of type GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, or GL_UNSIGNED_INT.
Related
Take inputs from keyboard such as "+" or "-" and increase/decrease sides accordingly. For example if a triangle is currently displayed and if i press "+", it should transform into a rectangle etc. How can I achieve that?
static void DisplayShape(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glColor3d(1,0.1,0.6);
glBegin(GL_POINTS);
for(int i=0;i<n;++i) // n - sides count
{
glVertex2f(); // the n-sided shape is to be drawn here
}
glEnd();
glutSwapBuffers();
}
static void key(unsigned char key, int x, int y)
{
switch (key)
{
case 27 :
case 'q': // quit
exit(0);
break;
case '+': // increase sides count
n++;
break;
case '-': // decrease sides count
if (n > 3) // cannot be less than 3
{
n--;
}
break;
}
glutPostRedisplay();
}
static void idle(void)
{
glutPostRedisplay();
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitWindowSize(640,480);
glutInitWindowPosition(10,10);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutCreateWindow("GLUT Shapes");
glutDisplayFunc(DisplayShape);
glutKeyboardFunc(key);
glutIdleFunc(idle);
glutMainLoop();
return EXIT_SUCCESS;
}
Distribute the N-points around a circle. Compute the angle between the vectors from the center of the circle to the points (360°/N). Calculate the points using their Polar Coordinates:
const float x0 = 0.0f;
const float y0 = 0.0f;
const float sideLen = 0.5;
float dist = sideLen / 2.0f / sin(M_PI * 2.0f / n / 2.0f);
float startAngle = -M_PI * (n - 2) / 2 / n;
glBegin(GL_LINE_LOOP);
for (int i = 0; i < n; ++i) // n - sides count
{
float sideAngle = M_PI * 2.0 * i / n + startAngle;
float x = x0 + dist * cos(sideAngle);
float y = y0 + dist * sin(sideAngle);
glVertex2f(x, y);
}
glEnd();
Complete example:
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/freeglut.h>
#define _USE_MATH_DEFINES
#include <math.h>
int n = 3;
static void DisplayShape(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glColor3d(1, 0.1, 0.6);
const float x0 = 0.0f;
const float y0 = 0.0f;
const float sideLen = 0.5;
float dist = sideLen / 2.0f / sin(M_PI * 2.0f / n / 2.0f);
float startAngle = -M_PI * (n - 2) / 2 / n;
glBegin(GL_LINE_LOOP);
for (int i = 0; i < n; ++i) // n - sides count
{
float sideAngle = M_PI * 2.0 * i / n + startAngle;
float x = x0 + dist * cos(sideAngle);
float y = y0 + dist * sin(sideAngle);
glVertex2f(x, y);
}
glEnd();
glutSwapBuffers();
glutPostRedisplay();
}
static void key(unsigned char key, int x, int y)
{
switch (key)
{
case 27:
case 'q': // quit
exit(0);
break;
case '+': // increase sides count
n++;
break;
case '-': // decrease sides count
if (n > 3) // cannot be less than 3
n--;
break;
}
glutPostRedisplay();
}
static void reshape(int width, int height)
{
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
double aspect = (double)width / height;
glOrtho(-aspect, aspect, -1.0, 1.0, -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitWindowSize(640, 480);
glutInitWindowPosition(10, 10);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutCreateWindow("GLUT Shapes");
glutReshapeFunc(reshape);
glutDisplayFunc(DisplayShape);
glutKeyboardFunc(key);
glutMainLoop();
return EXIT_SUCCESS;
}
After the camera rotation on the Y axis and X and the subsequent move, is a strange camera rotation along the axis Z. For example, here is the normal state but I was randomly moved around the stage, all twisted I do not know what to do and how to fix the problem, I hope for your help. I saw this question, it does not help me, because I do not even use glm, do not mark as a duplicate.
Code:
#include <iostream>
#include <chrono>
#include <GL/glut.h>
#include "Camera.h"
using namespace std;
constexpr auto FPS_RATE = 120;
int windowHeight = 600, windowWidth = 600, windowDepth = 600;
float angle = 0, speedRatio = 0.25;
struct MyPoint3f
{
float x;
float y;
float z;
};
MyPoint3f lastMousePos = { };
bool mouseButtonWasPressed = false;
float mouseSensitivity = 0.1;
float camMoveSpeed = 3;
float camPitchAngle = 0, camYawAngle = 0;
Camera cam;
void init();
void displayFunction();
void idleFunction();
void reshapeFunction(int, int);
void keyboardFunction(unsigned char, int, int);
void specialKeysFunction(int, int, int);
void mouseFunc(int, int, int, int);
void motionFunction(int, int);
double getTime();
double getTime()
{
using Duration = std::chrono::duration<double>;
return std::chrono::duration_cast<Duration>(
std::chrono::high_resolution_clock::now().time_since_epoch()
).count();
}
const double frame_delay = 1.0 / FPS_RATE;
double last_render = 0;
void init()
{
glutDisplayFunc(displayFunction);
glutIdleFunc(idleFunction);
glutReshapeFunc(reshapeFunction);
glutKeyboardFunc(keyboardFunction);
glutMouseFunc(mouseFunc);
glutMotionFunc(motionFunction);
glViewport(0, 0, windowWidth, windowHeight);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-windowWidth / 2, windowWidth / 2, -windowHeight / 2, windowHeight / 2, -windowDepth / 2, windowDepth / 2);
glClearColor(0.0, 0.0, 0.0, 0.0);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
cam.setShape(45, (double)windowWidth / windowHeight, 0.1, 1000);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
cam.set(Point3(0, 0, 350), Point3(0, 0, 349), Vector3(0, 1, 0));
}
void displayFunction()
{
angle += speedRatio;
if (angle >= 360 || angle <= -360) angle = 0;
if (camPitchAngle <= -360) camPitchAngle = 0;
if (camPitchAngle >= 360) camPitchAngle = 0;
if (camYawAngle <= -360) camYawAngle = 0;
if (camYawAngle >= 360) camYawAngle = 0;
cout << camPitchAngle << " " << camYawAngle << endl;
cam.pitch(-(camPitchAngle *= mouseSensitivity));
cam.yaw(-(camYawAngle *= mouseSensitivity));
camPitchAngle = 0; camYawAngle = 0;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glRotatef(angle, 1, 0, 0);
glRotatef(angle, 0, 1, 0);
glColor3f(0, 1, 0);
glutWireCube(150.0);
glBegin(GL_LINES);
glColor3f(1, 0, 0);
for (int i = 0; i <= 75; i += 5)
{
glVertex3i(i, 0, 0);
glVertex3i(-i, 0, 0);
glVertex3i(0, i, 0);
glVertex3i(0, -i, 0);
glVertex3i(0, 0, i);
glVertex3i(0, 0, -i);
}
glEnd();
glPopMatrix();
//RSHIFT and CTRL
if (GetAsyncKeyState(VK_LSHIFT))
{
cam.slide(0, 1.0 * camMoveSpeed, 0);
}
if (GetAsyncKeyState(VK_LCONTROL))
{
cam.slide(0, -1.0 * camMoveSpeed, 0);
}
glutSwapBuffers();
}
void idleFunction()
{
const double current_time = getTime();
if ((current_time - last_render) > frame_delay)
{
last_render = current_time;
glutPostRedisplay();
}
}
void reshapeFunction(int w, int h)
{
}
void keyboardFunction(unsigned char key, int w, int h)
{
switch (key)
{
case '+': case '=':
speedRatio += 0.125;
break;
case '-': case '_':
speedRatio -= 0.125;
break;
case 'A': case 'a':
cam.slide(-1.0 * camMoveSpeed, 0, 0);
break;
case 'D': case 'd':
cam.slide(1.0 * camMoveSpeed, 0, 0);
break;
case 'W': case 'w':
cam.slide(0, 0, -1.0 * camMoveSpeed);
break;
case 'S': case 's':
cam.slide(0, 0, 1.0 * camMoveSpeed);
break;
case 'Z': case 'z':
cam.yaw(-1);
break;
case 'X': case 'x':
cam.yaw(1);
break;
case 27:
angle = 0;
speedRatio = 0;
cam.set(Point3(0, 0, 350), Point3(0, 0, 349), Vector3(0, 1, 0));
break;
default:
cout << key << endl;
break;
}
}
void specialKeysFunction(int key, int x, int y)
{
cout << key << endl;
}
void mouseFunc(int button, int state, int x, int y)
{
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
{
mouseButtonWasPressed = true;
lastMousePos.x = x;
lastMousePos.y = y;
}
}
void motionFunction(int mousePosX, int mousePosY)
{
if (mousePosX >= 0 && mousePosX < windowWidth && mousePosY >= 0 && mousePosY < windowHeight)
{
if (mouseButtonWasPressed)
{
camPitchAngle += -mousePosY + lastMousePos.y;
camYawAngle += mousePosX - lastMousePos.x;
lastMousePos.x = mousePosX;
lastMousePos.y = mousePosY;
}
}
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(windowWidth, windowHeight);
glutInitWindowPosition((GetSystemMetrics(SM_CXSCREEN) - windowWidth) / 2, (GetSystemMetrics(SM_CYSCREEN) - windowHeight) / 2);
glutCreateWindow("Window");
init();
glutMainLoop();
return 0;
}
Camera.h
Camera.cpp
When you do
glRotatef(angle, 1, 0, 0);
glRotatef(angle, 0, 1, 0);
Then the model is rotated around the y-axis and then the rotated model is rotated around the x-axis, because glRotatef sets a rotation matrix and multiplies it to the current matrix.
Because the model is rotated around the y-axis before it is rotated around the x-axis, the (view space) y-axis is kept it the yz-plane of the view space.
If you want to keep the x-axis in the xz-plane of the view space, you've to do the rotation around the x-axis first:
glRotatef(angle, 0, 1, 0);
glRotatef(angle, 1, 0, 0);
The same issue occurs when you apply pich() and yaw() to the camera object. If you switch it (first yaw() then pitch()), then that won't solve the issue, because pitch and yaw are applied incrementally by each mouse move (pich(), yaw(), pich(), yaw(), pich(), yaw() ...). So there is always a yaw() after a pitch() and the model gets tilted.
To solve the issue you've to sum up camPitchAngle and camYawAngle. Take into account the mouse intensity:
void motionFunction(int mousePosX, int mousePosY)
{
if (mousePosX >= 0 && mousePosX < windowWidth && mousePosY >= 0 && mousePosY < windowHeight)
{
if (mouseButtonWasPressed)
{
camPitchAngle += (-mousePosY + lastMousePos.y) * mouseSensitivity;
camYawAngle += (mousePosX - lastMousePos.x) * mouseSensitivity;
lastMousePos.x = mousePosX;
lastMousePos.y = mousePosY;
}
}
}
Copy the camera object (cam / curr_cam) in displayFunction and apply camPitchAngle and camYawAngle to the copy. Use the copy to set the view and projection matrix:
void displayFunction()
{
// [...]
// cam.pitch(-(camPitchAngle *= mouseSensitivity)); <--- delete
// cam.yaw(-(camYawAngle *= mouseSensitivity)); <--- delete
// [...]
Camera curr_cam = cam;
curr_cam.yaw( -camYawAngle );
curr_cam.pitch( -camPitchAngle );
// [...]
if (GetAsyncKeyState(VK_LSHIFT))
{
curr_cam.slide(0, 1.0 * camMoveSpeed, 0);
}
if (GetAsyncKeyState(VK_LCONTROL))
{
curr_cam.slide(0, -1.0 * camMoveSpeed, 0);
}
// [...]
}
Of course you've to set camYawAngle = 0 respectively camPitchAngle = 0; when z, x or ESC is pressed.
I am having trouble with this code. I want it to fire fireworks that explode and fall in front of a static background. Right now the fireworks and background work, but together they cause a flickering and the fireworks don't fall at a regular rate. How do I prevent this flickering and achieve a regular rate of firework's fall?
#include <GL/freeglut.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <iomanip>
#include "Header.h"
using namespace std;
#define M_PI (3.1415926535897932384626433832795)
GLfloat randomNum()
{
return (rand() % 10000) / 10000.0;
}
GLfloat nx = 0;
GLfloat ny = .8;
#define MAX_POINTS 750
GLfloat numPoints;
GLfloat curx, cury;
GLfloat x[MAX_POINTS], y[MAX_POINTS];
GLfloat xacc[MAX_POINTS], yacc[MAX_POINTS];
GLfloat red, green, blue;
int step; int length;
GLfloat newRed = 0;
GLfloat newGreen = 0;
GLfloat newBlue = 0;
GLint totPor = 0;
GLint strontiumPor = 0;
GLfloat bariumPor = 0;
GLfloat copperPor = 0;
GLfloat sodiumPor = 0;
GLfloat phosPor = 0;
GLfloat red2;
GLfloat green2;
GLfloat green3;
GLfloat blue2;
void initialize()
{
int j; double temp, temp2;
numPoints = randomNum()*(MAX_POINTS - 1);
curx = nx;
cury = ny;
//Color Mixing
if (totPor != 0)
{
red = newRed * strontiumPor / (totPor - strontiumPor); //s red
green = newGreen * bariumPor / (totPor - bariumPor); //b green
blue = newBlue * copperPor / (totPor - copperPor); //c blue
red2 = newRed * sodiumPor / (totPor - sodiumPor); //d yellow
green2 = newGreen * sodiumPor / (totPor - sodiumPor);
green3 = newGreen * phosPor / (totPor - phosPor); //p blue green
blue2 = newBlue * phosPor / (totPor - phosPor);
red = red + red2;
green = green + green2 + green3;
blue = blue + blue2;
}
else
{
red = newRed;
green = newGreen;
blue = newBlue;
}
glPointSize(1.7);
step = 0;
length = 500 + 300 * randomNum();
/* initialize the blast */
for (j = 0; j<numPoints; j++) {
x[j] = curx;
y[j] = cury;
temp = randomNum();
temp2 = randomNum()*2.0*M_PI;
xacc[j] = (cos(temp2) * temp) / length;
yacc[j] = (sin(temp2) * temp) / length;
}
}
void draw_fireworks(void)
{
int i;
double glow = (length - (step)) / (double)length;
glColor3f(red*glow, green*glow, blue*glow); //glow
glBegin(GL_POINTS);
for (i = 0; i<numPoints; i++) {
x[i] += xacc[i];
y[i] += yacc[i];
glVertex2f(x[i], y[i]);
}
glEnd();
glFlush();
glutSwapBuffers();
}
void display(void)
{
int i;
glClear(GL_COLOR_BUFFER_BIT);
if (step < 0.9*length) {
for (i = 0; i<numPoints; i++)
yacc[i] -= 0.02 / length; // gravity
draw_fireworks();
}
step++;
if (step > length) initialize();
DrawScene();
glutSwapBuffers();
}
int t = 0;
void idle(void)
{
if (t == 45000)
{
glutPostRedisplay();
t = 0;
}
t++;
}
void SpecialKey(int key, int x, int y)
{
switch (key) {
case GLUT_KEY_LEFT:
/* Move fireworks left or right, up or down */
nx = nx + -.025;
break;
case GLUT_KEY_RIGHT:
nx = nx + .025;
break;
case GLUT_KEY_UP:
ny = ny + .025;
break;
case GLUT_KEY_DOWN:
ny = ny + -.025;
break;
}
glutPostRedisplay();
}
void Keyboard(unsigned char key, int x, int y)
{
//Select Chemicals
switch (key)
{
case 's':
newRed = 1;
strontiumPor = strontiumPor + 1;
totPor = totPor + 1;
break;
case 'b':
newGreen = 1;
bariumPor = bariumPor + 1;
totPor = totPor + 1;
break;
case 'c':
newBlue = 1;
copperPor = copperPor + 1;
totPor = totPor + 1;
break;
case 'd':
newRed = 1;
newGreen = 1;
sodiumPor = sodiumPor + 1;
totPor = totPor + 1;
break;
case 'p':
newBlue = 1;
newGreen = 1;
phosPor = phosPor + 1;
totPor = totPor + 1;
break;
case 'R': newRed = newRed + .1;
break;
case 'G': newGreen = newGreen + .1;
break;
case 'B': newBlue = newBlue + .1;
break;
case ' ': //Space bar is Reset or start
newRed = 1;
newGreen = 1;
newBlue = 1;
totPor = 0;
strontiumPor = 0;
bariumPor = 0;
copperPor = 0;
sodiumPor = 0;
phosPor = 0;
break;
case 'm': nx = 0; ny = 0.8; //M resets target to default
break;
case 'q': exit(0); //Q is quit
}
glutPostRedisplay();
}
void reshape(int w, int h)
{
glViewport(0, 0, (GLsizei)w, (GLsizei)h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (w <= h)
glOrtho(-1.0, 1.0,
-1.0*(GLfloat)h / (GLfloat)w, 1.0*(GLfloat)h / (GLfloat)w,
-1.0, 1.0);
else
glOrtho(-1.0*(GLfloat)w / (GLfloat)h, 1.0*(GLfloat)w / (GLfloat)h,
-1.0, 1.0,
-1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(700, 700);
glutInitWindowPosition(0, 0);
glutCreateWindow("Fireworks Display");
glClearColor(0.0, 0.0, 0.0, 0.0);
initialize();
initText();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutIdleFunc(idle);
glutKeyboardFunc(Keyboard);
glutSpecialFunc(SpecialKey);
glutMainLoop();
return 0;
}
And then also this:
#include <GL/freeglut.h>
#include "Soil.h"
GLuint tex_ID;
void LoadTextureMap()
{
int width, height, channels;
unsigned char* image = SOIL_load_image("washington.png", &width, &height, &channels, SOIL_LOAD_AUTO);
glGenTextures(1, &tex_ID);
glBindTexture(GL_TEXTURE_2D, tex_ID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, channels, width, height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, image);
SOIL_free_image_data(image);
}
void Tree(GLfloat x, GLfloat y, GLfloat z)
{
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, tex_ID);
glPushMatrix();
glRotatef(180, 0.0f, 0.0f, 1.0f);
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_QUADS);
glTexCoord2f(0.0, 0.0); glVertex3f(-1.0 + x, -1.0 + y, 1.0 + z);
glTexCoord2f(0.0, 1.0); glVertex3f(-1.0 + x, 1.0 + y, 1.0 + z);
glTexCoord2f(1.0, 1.0); glVertex3f(1 + x, 1.0 + y, 1.0 + z);
glTexCoord2f(1.0, 0.0); glVertex3f(1 + x, -1.0 + y, 1.0 + z);
glEnd();
glPopMatrix();
glDisable(GL_TEXTURE_2D);
}
void DrawScene()
{
Tree(0, 0, 0);
}
void initText()
{
LoadTextureMap();
}
You're calling glutSwapBuffers() in draw_fireworks() and also in display(). You should call it only once per frame.
Btw, if you want to learn OpenGL don't waste your time with this legacy fixed function pipeline stuff. Go with shaders instead.
I have a problem with my opengl project. I have to create moving triangle alonge x-axis. It laso has to change its colour and turn around. I have created this but i don`t know what is going on with acceleration...
#include <stdio.h>
#include <stdlib.h>
#include<iostream>
#include <math.h>
#include<iomanip>
#include <time.h>
#include "glut.h"
#include "math.h"
using namespace std;
float x = 0, y = 0, angle = 0, anglen = 0,q=1,w=1,e=1,qn=0,wn=0,en=0;
double xn = 0,c=0;
int licznik = 0;
int zmiana = 0,a=0;
bool flaga = true;
static void timerCallback(int value)
{
if (xn!=x)
{
licznik++; //odliczanie kolejnych klatek
x += xn / 100.0;
angle += anglen / 100.0;
q += qn / 100.0;
w += wn / 100.0;
e += en / 100.0;
if (q > 1){ q = 0; }
if (w > 1){ w = 0; }
if (e > 1){ e = 0; }
glutPostRedisplay();
glutTimerFunc(50, timerCallback, value); //ustawienie ponownego wywołania naszej funkcji "timerCallback" po 100 ms
}
if (licznik==100)
{
if (a == 0){ x = -0.8; glutPostRedisplay(); }
else if (a == 1){ x = 0; glutPostRedisplay(); }
else if (a == 2){ x = 0.8; glutPostRedisplay(); }
else if (a == 3){ x = 0; glutPostRedisplay(); }
xn = 0;
anglen = 0;
licznik = 0;
zmiana = 1;
}
}
void trojkat(float x, float y, float angle)
{
glColor3f(q, w, e);
glPushMatrix();
glTranslatef(x, y, 0);
glRotatef(angle, 0, 0, 1);
glBegin(GL_POLYGON);
glVertex2f(0, 0.2);
glVertex2f(-0.2, -0.2);
glVertex2f(0.2, -0.2);
glEnd();
glPopMatrix();
}
void przesuwanie()
{
a++;
if (a >3){ a = 0; };
if ((a == 1) || (a == 2)){ flaga = true; }
else if ((a == 0) || (a == 3)){flaga = false;}
if (flaga == true)
{
xn += .8; anglen += 360;
qn += .1;
wn += .7;
en += .3;
}
else if (flaga == false)
{
xn -= .8; anglen += 360;
qn += .8;
wn += .4;
en += .3;
}
zmiana = 4;
//wyzerowanie zliczania klatek
timerCallback(0); //wywołanie funkcji która będzie się okresowo powtarzać
}
void przesuwanie_w_lewo()// pierwsze przesuniecie o połowe jednostki
{
xn = 0;
xn -= .8;
anglen += 360;
qn -= .9;
wn -= .7;
en -= .3;
zmiana = 4;
timerCallback(0);
//wywołanie funkcji która będzie się okresowo powtarzać
}
void display(void)
{
/* clear window */
glClear(GL_COLOR_BUFFER_BIT);
trojkat(x, y, angle);
glFlush();
if ((zmiana == 0) || (zmiana == 1) || (zmiana == 2))
{
if (zmiana == 0)
{
przesuwanie_w_lewo();
}
else if (zmiana==1)
{
przesuwanie();
}
}
}
void init()
{
/* set clear color to black */
glClearColor(0.0, 0.0, 0.0, 0.0);
/* set fill color to white */
/* set up standard orthogonal view with clipping */
/* box as cube of side 2 centered at origin */
/* This is default view and these statement could be removed */
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(400, 400);
glutInitWindowPosition(0, 0);
glutCreateWindow("trojkat");
glutDisplayFunc(display);
init();
glutMainLoop();
return 0;
}
I am using gl and glut on Windows platform
my problem is that glReadPixels returns all 0s. I guess it has something to do with the way I initialize the window so it cannot get the correct pixel value.
This is how I initialize the window:
glutInit(&argc, argv);
glutInitWindowSize(800,600);
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode (GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 800, 600, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glTranslatef(0.375, 0.375, 0);
glClearColor(0, 0, 0, 1.0);
And with this, I get all 0s:
Edit:
glColor3f(1.0, 0.0, 0.0);
glBegin(GL_QUADS);
glVertex2i(x-20, y-20);
glVertex2i(x-20, y+20);
glVertex2i(x+20, y+20);
glVertex2i(x+20, y-20);
glEnd();
unsigned char pixel[4];
glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
After I glClear, I did render some shapes, at (x, y) then I used glReadPixels to get the color at (x, y) but it returns 0s. I tried to glReadPixels the whole screen and it returns 0s too.
Edit 2:
So, to be more clear about my problem, here is the code:
I just don't know where the source of the problem could be so I pasted all of the code here. This is the tanks program from the book "Game Programming All in One" by Jonathan S. Habour. The book uses Allegro library, I try to convert to openGL. At the "look for a hit" in the updatebullet procedure, I printed out the coordinates of the enemy tank and the color at that pixel but all I get is 0s.
#include <GL/glut.h>
#include <stdlib.h>
#include <iostream.h>
#include <windows.h>
//define tank structure
struct tagTank
{
int x,y;
int dir,speed;
} tanks[2];
struct tagBullet
{
int x,y;
int alive;
int xspd,yspd;
} bullets[2];
void setuptanks()
{
tanks[0].x = 30;
tanks[0].y = 40;
tanks[0].dir = 1;
tanks[0].speed = 5;
tanks[1].x = 800 - 30;
tanks[1].y = 600 - 30;
tanks[1].dir = 3;
tanks[1].speed = 5;
}
void drawtank(int num)
{
int x = tanks[num].x;
int y = tanks[num].y;
int dir = tanks[num].dir;
//draw tank body
glColor3f(1.0, 0.0, 0.0);
if (num) glColor3f(0.0, 0.0, 1.0);
glBegin(GL_QUADS);
glVertex2i(x-20, y-20);
glVertex2i(x-20, y+20);
glVertex2i(x+20, y+20);
glVertex2i(x+20, y-20);
glEnd();
glColor3f(0.5, 0.0, 0.0);
if (num) glColor3f(0.0, 0.0, 0.5);
glBegin(GL_QUADS);
glVertex2i(x-10, y-10);
glVertex2i(x-10, y+10);
glVertex2i(x+10, y+10);
glVertex2i(x+10, y-10);
glEnd();
//draw the turret based on direction
glColor3f(1.0, 1.0, 1.0);
switch (dir)
{
case 0:
glBegin(GL_QUADS);
glVertex2i(x-2, y-30);
glVertex2i(x-2, y);
glVertex2i(x+2, y);
glVertex2i(x+2, y-30);
glEnd();
break;
case 1:
glBegin(GL_QUADS);
glVertex2i(x, y-2);
glVertex2i(x, y+2);
glVertex2i(x+30, y+2);
glVertex2i(x+30, y-2);
glEnd();
break;
case 2:
glBegin(GL_QUADS);
glVertex2i(x-2, y);
glVertex2i(x-2, y+30);
glVertex2i(x+2, y+30);
glVertex2i(x+2, y);
glEnd();
break;
case 3:
glBegin(GL_QUADS);
glVertex2i(x-30, y-2);
glVertex2i(x-30, y+2);
glVertex2i(x, y+2);
glVertex2i(x, y-2);
glEnd();
break;
}
}
void erasetank(int num)
{
//calculate box to encompass the tank
int left = tanks[num].x - 30;
int top = tanks[num].y - 30;
int right = tanks[num].x + 30;
int bottom = tanks[num].y + 30;
//erase the tank
glColor3f(0.0, 0.0, 0.0);
glBegin(GL_QUADS);
glVertex2i(left, top);
glVertex2i(left, bottom);
glVertex2i(right, bottom);
glVertex2i(right, top);
glEnd();
}
void movetank(int num)
{
int dir = tanks[num].dir;
int speed = tanks[num].speed;
//update tank position based on direction
switch(dir)
{
case 0:
tanks[num].y -= speed;
break;
case 1:
tanks[num].x += speed;
break;
case 2:
tanks[num].y += speed;
break;
case 3:
tanks[num].x -= speed;
break;
}
//keep tank inside the screen
if (tanks[num].x > 800-30)
{
tanks[num].x = 800-30;
tanks[num].speed = 0;
}
else if (tanks[num].x < 30)
{
tanks[num].x = 30;
tanks[num].speed = 0;
}
else if (tanks[num].y > 600-30)
{
tanks[num].y = 600-30;
tanks[num].speed = 0;
}
else if (tanks[num].y < 30)
{
tanks[num].y = 30;
tanks[num].speed = 0;
}
else tanks[num].speed = 5;
}
void explode(int num, int x, int y)
{
int n;
//retrieve location of enemy tank
int tx = tanks[!num].x;
int ty = tanks[!num].y;
//is bullet inside the boundary of the enemy tank?
if (x > tx-30 && x < tx+30 && y > ty-30 && y < ty+30)
setuptanks();
//draw some random circles for the "explosion"
for (n = 0; n < 10; n++)
{
glColor3f((rand() % 101)/100.0, (rand() % 101)/100.0, (rand() % 101)/100.0);
glBegin(GL_QUADS);
glVertex2i(x-16, y-16);
glVertex2i(x-16, y+16);
glVertex2i(x+16, y+16);
glVertex2i(x+16, y-16);
glEnd();
//Sleep(10);
}
//clear the area of debris
glColor3f(0.0, 0.0, 0.0);
glBegin(GL_QUADS);
glVertex2i(x-16, y-16);
glVertex2i(x-16, y+16);
glVertex2i(x+16, y+16);
glVertex2i(x+16, y-16);
glEnd();
}
void updatebullet(int num)
{
int x = bullets[num].x;
int y = bullets[num].y;
if (bullets[num].alive)
{
//erase bullet
glColor3f(0.0, 0.0, 0.0);
glBegin(GL_QUADS);
glVertex2i(x-2, y-2);
glVertex2i(x-2, y+2);
glVertex2i(x+2, y+2);
glVertex2i(x+2, y-2);
glEnd();
//move bullet
bullets[num].x += bullets[num].xspd;
bullets[num].y += bullets[num].yspd;
x = bullets[num].x;
y = bullets[num].y;
//stay within the screen
if (x < 5 || x > 800 || y < 20 || y > 600)
{
bullets[num].alive = 0;
return;
}
//look for a hit
unsigned char pixel[4];
glReadPixels(tanks[!num].x, tanks[!num].y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
cout << tanks[!num].x << ", " << tanks[!num].y << " | " << (int)pixel[0] << ", " << (int)pixel[1] << ", " << (int)pixel[2] << endl;
if ((int)pixel[0] || (int)pixel[1] || (int)pixel[2])
{
bullets[num].alive = 0;
explode(num, x, y);
return;
}
//draw bullet
x = bullets[num].x;
y = bullets[num].y;
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_QUADS);
glVertex2i(x-2, y-2);
glVertex2i(x-2, y+2);
glVertex2i(x+2, y+2);
glVertex2i(x+2, y-2);
glEnd();
}
}
void fireweapon(int num)
{
int x = tanks[num].x;
int y = tanks[num].y;
//ready to fire again?
if (!bullets[num].alive)
{
bullets[num].alive = 1;
//fire bullet in direction tank is facing
switch (tanks[num].dir)
{
//north
case 0:
bullets[num].x = x;
bullets[num].y = y-30;
bullets[num].xspd = 0;
bullets[num].yspd = -20;
break;
//east
case 1:
bullets[num].x = x+30;
bullets[num].y = y;
bullets[num].xspd = 20;
bullets[num].yspd = 0;
break;
//south
case 2:
bullets[num].x = x;
bullets[num].y = y+30;
bullets[num].xspd = 0;
bullets[num].yspd = 20;
break;
//west
case 3:
bullets[num].x = x-30;
bullets[num].y = y;
bullets[num].xspd = -20;
bullets[num].yspd = 0;
break;
}
}
}
void up(int num)
{
tanks[num].dir = 0;
}
void down(int num)
{
tanks[num].dir = 2;
}
void left(int num)
{
tanks[num].dir = 3;
}
void right(int num)
{
tanks[num].dir = 1;
}
static void display(void)
{
erasetank(0);
erasetank(1);
movetank(0);
movetank(1);
drawtank(0);
drawtank(1);
updatebullet(0);
updatebullet(1);
glFlush();
Sleep(50);
}
static void key(unsigned char key, int x, int y)
{
switch (key)
{
case 27 :
exit(0);
break;
case 'a':
left(1);
break;
case 'd':
right(1);
break;
case 'w':
up(1);
break;
case 's':
down(1);
break;
case 32:
fireweapon(1);
break;
case 13:
fireweapon(0);
break;
}
glutPostRedisplay();
}
static void specialkey(int key, int x, int y)
{
switch (key)
{
case GLUT_KEY_LEFT:
left(0);
break;
case GLUT_KEY_RIGHT:
right(0);
break;
case GLUT_KEY_UP:
up(0);
break;
case GLUT_KEY_DOWN:
down(0);
break;
}
glutPostRedisplay();
}
static void idle(void)
{
glutPostRedisplay();
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitWindowSize(800,600);
glutInitWindowPosition(10,10);
glutCreateWindow("Tanks");
glClear(GL_COLOR_BUFFER_BIT);
glutDisplayFunc(display);
glutKeyboardFunc(key);
glutSpecialFunc(specialkey);
glutIdleFunc(idle);
glMatrixMode (GL_PROJECTION);
glLoadIdentity();
glOrtho(0, 800, 600, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glTranslatef(0.375, 0.375, 0);
glClearColor(0, 0, 0, 1.0);
setuptanks();
glutMainLoop();
return EXIT_SUCCESS;
}
As Roger Rowland pointed out you are reading from the wrong part of the screen. You need to flip your y-coordinate before you pass it into glReadPixels()
Other things:
You don't need erasetank() or erasebullet(), just clear the screen each frame
It's a good idea to put glClear() and your matrix stuff in the display callback
An explicit glutInitDisplayMode() is a good idea
There's no real reason not to use GLUT_DOUBLE
Use a glutTimerFunc() instead of sleep()
All together:
#include <GL/glut.h>
#include <iostream>
using namespace std;
//define tank structure
struct tagTank
{
int x,y;
int dir,speed;
} tanks[2];
struct tagBullet
{
int x,y;
int alive;
int xspd,yspd;
} bullets[2];
void setuptanks()
{
tanks[0].x = 30;
tanks[0].y = 40;
tanks[0].dir = 1;
tanks[0].speed = 5;
tanks[1].x = 800 - 30;
tanks[1].y = 600 - 30;
tanks[1].dir = 3;
tanks[1].speed = 5;
}
void drawtank(int num)
{
int x = tanks[num].x;
int y = tanks[num].y;
int dir = tanks[num].dir;
//draw tank body
glColor3f(1.0, 0.0, 0.0);
if (num) glColor3f(0.0, 0.0, 1.0);
glBegin(GL_QUADS);
glVertex2i(x-20, y-20);
glVertex2i(x-20, y+20);
glVertex2i(x+20, y+20);
glVertex2i(x+20, y-20);
glEnd();
glColor3f(0.5, 0.0, 0.0);
if (num) glColor3f(0.0, 0.0, 0.5);
glBegin(GL_QUADS);
glVertex2i(x-10, y-10);
glVertex2i(x-10, y+10);
glVertex2i(x+10, y+10);
glVertex2i(x+10, y-10);
glEnd();
//draw the turret based on direction
glColor3f(1.0, 1.0, 1.0);
switch (dir)
{
case 0:
glBegin(GL_QUADS);
glVertex2i(x-2, y-30);
glVertex2i(x-2, y);
glVertex2i(x+2, y);
glVertex2i(x+2, y-30);
glEnd();
break;
case 1:
glBegin(GL_QUADS);
glVertex2i(x, y-2);
glVertex2i(x, y+2);
glVertex2i(x+30, y+2);
glVertex2i(x+30, y-2);
glEnd();
break;
case 2:
glBegin(GL_QUADS);
glVertex2i(x-2, y);
glVertex2i(x-2, y+30);
glVertex2i(x+2, y+30);
glVertex2i(x+2, y);
glEnd();
break;
case 3:
glBegin(GL_QUADS);
glVertex2i(x-30, y-2);
glVertex2i(x-30, y+2);
glVertex2i(x, y+2);
glVertex2i(x, y-2);
glEnd();
break;
}
}
void movetank(int num)
{
int dir = tanks[num].dir;
int speed = tanks[num].speed;
//update tank position based on direction
switch(dir)
{
case 0:
tanks[num].y -= speed;
break;
case 1:
tanks[num].x += speed;
break;
case 2:
tanks[num].y += speed;
break;
case 3:
tanks[num].x -= speed;
break;
}
//keep tank inside the screen
if (tanks[num].x > 800-30)
{
tanks[num].x = 800-30;
tanks[num].speed = 0;
}
else if (tanks[num].x < 30)
{
tanks[num].x = 30;
tanks[num].speed = 0;
}
else if (tanks[num].y > 600-30)
{
tanks[num].y = 600-30;
tanks[num].speed = 0;
}
else if (tanks[num].y < 30)
{
tanks[num].y = 30;
tanks[num].speed = 0;
}
else tanks[num].speed = 5;
}
void explode(int num, int x, int y)
{
int n;
//retrieve location of enemy tank
int tx = tanks[!num].x;
int ty = tanks[!num].y;
//is bullet inside the boundary of the enemy tank?
if (x > tx-30 && x < tx+30 && y > ty-30 && y < ty+30)
setuptanks();
//draw some random circles for the "explosion"
for (n = 0; n < 10; n++)
{
glColor3f((rand() % 101)/100.0, (rand() % 101)/100.0, (rand() % 101)/100.0);
glBegin(GL_QUADS);
glVertex2i(x-16, y-16);
glVertex2i(x-16, y+16);
glVertex2i(x+16, y+16);
glVertex2i(x+16, y-16);
glEnd();
}
}
void updatebullet(int num)
{
if (bullets[num].alive)
{
//move bullet
bullets[num].x += bullets[num].xspd;
bullets[num].y += bullets[num].yspd;
int x = bullets[num].x;
int y = bullets[num].y;
//stay within the screen
if (x < 5 || x > 800 || y < 20 || y > 600)
{
bullets[num].alive = 0;
return;
}
//look for a hit
unsigned char pixel[4];
int h = glutGet( GLUT_WINDOW_HEIGHT );
glReadPixels(x, h - y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
cout << x << ", " << y << " | " << (int)pixel[0] << ", " << (int)pixel[1] << ", " << (int)pixel[2] << endl;
if ((int)pixel[0] || (int)pixel[1] || (int)pixel[2])
{
bullets[num].alive = 0;
explode(num, x, y);
return;
}
//draw bullet
glColor3f(1.0, 1.0, 1.0);
glBegin(GL_QUADS);
glVertex2i(x-2, y-2);
glVertex2i(x-2, y+2);
glVertex2i(x+2, y+2);
glVertex2i(x+2, y-2);
glEnd();
}
}
void fireweapon(int num)
{
int x = tanks[num].x;
int y = tanks[num].y;
//ready to fire again?
if (!bullets[num].alive)
{
bullets[num].alive = 1;
//fire bullet in direction tank is facing
switch (tanks[num].dir)
{
//north
case 0:
bullets[num].x = x;
bullets[num].y = y-30;
bullets[num].xspd = 0;
bullets[num].yspd = -20;
break;
//east
case 1:
bullets[num].x = x+30;
bullets[num].y = y;
bullets[num].xspd = 20;
bullets[num].yspd = 0;
break;
//south
case 2:
bullets[num].x = x;
bullets[num].y = y+30;
bullets[num].xspd = 0;
bullets[num].yspd = 20;
break;
//west
case 3:
bullets[num].x = x-30;
bullets[num].y = y;
bullets[num].xspd = -20;
bullets[num].yspd = 0;
break;
}
}
}
void up(int num)
{
tanks[num].dir = 0;
}
void down(int num)
{
tanks[num].dir = 2;
}
void left(int num)
{
tanks[num].dir = 3;
}
void right(int num)
{
tanks[num].dir = 1;
}
static void display(void)
{
glClearColor(0, 0, 0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode (GL_PROJECTION);
glLoadIdentity();
int w = glutGet( GLUT_WINDOW_WIDTH );
int h = glutGet( GLUT_WINDOW_HEIGHT );
glOrtho( 0, w, h, 0, -1, 1 );
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.375, 0.375, 0);
movetank(0);
movetank(1);
drawtank(0);
drawtank(1);
glFlush();
updatebullet(0);
updatebullet(1);
glutSwapBuffers();
}
static void key(unsigned char key, int x, int y)
{
switch (key)
{
case 27 : exit(0); break;
case 'a': left(1); break;
case 'd': right(1); break;
case 'w': up(1); break;
case 's': down(1); break;
case 32: fireweapon(1); break;
case 13: fireweapon(0); break;
}
}
static void specialkey(int key, int x, int y)
{
switch (key)
{
case GLUT_KEY_LEFT: left(0); break;
case GLUT_KEY_RIGHT: right(0); break;
case GLUT_KEY_UP: up(0); break;
case GLUT_KEY_DOWN: down(0); break;
}
}
void timer( int value )
{
glutTimerFunc( 50, timer, 0 );
glutPostRedisplay();
}
int main(int argc, char *argv[])
{
glutInitWindowSize(800,600);
glutInit(&argc, argv);
glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE );
glutCreateWindow("Tanks");
glutDisplayFunc(display);
glutKeyboardFunc(key);
glutSpecialFunc(specialkey);
glutTimerFunc( 0, timer, 0 );
setuptanks();
glutMainLoop();
return EXIT_SUCCESS;
}