Linked ListsWouldn't it be nice to control each section of the tank as though they were separate sections and be able to have better control over the camera positioning?. Well, seeing as we have defined 3 objects that make up the tank - lets do just that.
Instead of going through the whole code listing line by line, I will only be explaining the new or changed lines of code - Most of this tutorial will deal with general coding rather than any OpenGL commands so feel free to change my code if you think you can program any faster routines.
Firstly we define a structure called 'CAM' - that will hold all the data to control the camera.
ROTX = Rotation angle for the X plane
ROTY = Rotation angle for the Y plane
ROTZ = Rotation angle for the Z plane
TRANX = Hold the Translation value for the X movement
TRANY = Hold the Translation value for the Y movement
TRANZ = Hold the Translation value for the Z movement
Type camera_data
Field rotx:Float
Field roty:Float
Field rotz:Float
Field tranx:Float
Field trany:Float
Field tranz:Float
End Type
Global cam:camera_data=New camera_data
Another structure this time called 'OBJECT', This time pointing to an array with enough space defined to hold the data for 5 shapes.
Notice that I've included some extra variables for scaling and offsetting the position of a shape. (We will use these in later tuts.)
Type object_data
Field rotx:Float
Field roty:Float
Field rotz:Float
Field tranx:Float
Field trany:Float
Field tranz:Float
Field scalex:Float
Field scaley:Float
Field scalez:Float
Field offsetx:Float
Field offsety:Float
Field offsetz:Float
End Type
Global obj:object_data[5]; For loop = 0 To 4
obj[loop] = New object_data; Next
Now that we have a structure for the camera, lets set up the camera to point to a good view of the object.
cam.tranz=-12
cam.rotx=20
cam.trany=-1
Over in the IDLE FUNCTION, I've added two lines of code:
These will increase the Y ROTATION values of both object 0 & 1, in other words the body and the turret. Seeing as we shall not be moving the gun itself separately from the other 2 parts of the shape, we do not need to change any of the rotation values for it. (Because it will take them from object 1 - the turret).
obj[0].roty+=0.01;
if obj[0].roty>360 obj[0].roty:-360.0;
obj[1].roty+=0.03;
if obj[1].roty>360 obj[1].roty:-360.0;
Now we come to the DISPLAY function, firstly I've added the code that will update our position within the world based on the cameras co-ordinates. This will move and rotate the entire world including ALL shapes and objects.
glTranslatef(cam.tranx,cam.trany,cam.tranz);
glRotatef(cam.rotx,1.0f,0.0f,0.0f);
glRotatef(cam.roty,0.0f,1.0f,0.0f);
glRotatef(cam.rotz,0.0f,0.0f,1.0f);
We now encase shape 0 (the body) in the two commands glPushMatrix() and glPopMatrix().
glPushMatrix() - this will save the current settings that were made when we moved/rotated the camera around the world to the new position. We do this so that we can then move certain shapes around without effecting the position of shapes drawn after it.
glPopMatrix() - restores the values we saved using the glPushMatrix command, overwriting any current values. if we didn't enclose it with these instructions all shapes that will be displayed will be rotated or translated by the previous shape. (Think of it as a knock-on effect)
Notice that I've only done it for the body section and not all 3 parts of the tank, this is because I don't want the gun to move independently to the turret. Maybe later on I will want to do this - but not for now. As the turret and the gun are the last 2 sections we are going to draw there is no need to store the world co-ordinates. (if there were more shapes to the world then YES, we would have to save the world co-ords with the Matrix commands)
After the glPushMatrix() instruction comes the commands that move and rotate the shape to its new position relative to the world. As you can see I'm adding an offset value to the translation, this is because later I may not wish to define a shape around the middle (the pivot). Using the offset I would be able to position the shape anywhere I wanted. (for example: The gun - later I could redesign it so that the pivot point is at the end rather than the middle and move it into place on the turret section using the offset values. )
// Body Section
glPushMatrix();
glTranslatef(obj[0].tranx+obj[0].offsetx, obj[0].trany+obj[0].offsety, obj[0].tranz+obj[0].offsetz)
glRotatef(obj[0].rotx, 1.0, 0, 0)
glRotatef(obj[0].roty, 0, 1.0, 0)
glRotatef(obj[0].rotz, 0, 0, 1.0)
glBegin(GL_QUADS);
glColor3f(0.3f,0.8f,0.2f); // Back Face
glVertex3f(4.0f, 1.0f, -2.0f);
......
glVertex3f(3.0f, -1.0f, 2.0f);
glEnd();
glPopMatrix();
// Turret Section
glPushMatrix()
glTranslatef(obj[1].tranx+obj[1].offsetx, obj[1].trany+obj[1].offsety, obj[1].tranz+obj[1].offsetz)
glRotatef(obj[1].rotx, 1.0, 0, 0)
glRotatef(obj[1].roty, 0, 1.0, 0)
glRotatef(obj[1].rotz, 0, 0, 1.0)
glColor3f(0.3,0.8,0.2); ' Back Face
......
Hopefully you have also noticed that the turret section is using the translate co-ordinates from shape 0 (the body) - this is because we want the the turret and gun to move with the body, so it's easier just to use the same co-ords.
You should by now be getting the general gist of it, just the gun section to go... this I leave up to you to complete ! ;)
So by now you should see a multi-jointed tank, rotating around the screen - Here's the complete listing incase you've had problems:
Global width:Int=800
Global height:Int=600
Type object_data
Field rotx:Float
Field roty:Float
Field rotz:Float
Field tranx:Float
Field trany:Float
Field tranz:Float
Field scalex:Float
Field scaley:Float
Field scalez:Float
Field offsetx:Float
Field offsety:Float
Field offsetz:Float
End Type
Type camera_data
Field rotx:Float
Field roty:Float
Field rotz:Float
Field tranx:Float
Field trany:Float
Field tranz:Float
End Type
Global cam:camera_data=New camera_data
Global obj:object_data[5]; For loop = 0 To 4; obj[loop] = New object_data; Next
cam.tranz=-12; cam.rotx=20; cam.trany=-1
bglCreateContext(width,height,32,0,BGL_BACKBUFFER|BGL_DEPTHBUFFER|BGL_FULLSCREEN)
init()
While Not KeyDown(KEY_ESCAPE)
display()
idle_function()
bglSwapBuffers
Wend
End
Function init()
glClearColor(0.0, 0.0, 0.0, 0.0)
glClearDepth(1.0)
glEnable(GL_CULL_FACE)
glEnable(GL_DEPTH_TEST)
glDepthFunc(GL_LEQUAL)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(45.0, Float(width)/Float(height), 0.1, 100.0)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
End Function
Function display()
glClear (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
glTranslatef(cam.tranx, cam.trany, cam.tranz)
glRotatef(cam.rotx,1.0,0.0,0.0)
glRotatef(cam.roty,0.0,1.0,0.0)
glRotatef(cam.rotz,0.0,0.0,1.0)
' Body
glPushMatrix()
glTranslatef(obj[0].tranx+obj[0].offsetx, obj[0].trany+obj[0].offsety, obj[0].tranz+obj[0].offsetz)
glRotatef(obj[0].rotx, 1.0, 0, 0)
glRotatef(obj[0].roty, 0, 1.0, 0)
glRotatef(obj[0].rotz, 0, 0, 1.0)
glBegin(GL_QUADS)
glColor3f(0.3, 0.8, 0.2)
glVertex3f( 4.0, 1.0,-2.0)
glVertex3f( 3.0,-1.0,-2.0)
glVertex3f( -3.0,-1.0,-2.0)
glVertex3f( -4.0, 1.0, -2.0)
glColor3f(0.3, 0.8, 0.2)
glVertex3f(-4.0, 1.0, 2.0)
glVertex3f(-3.0, -1.0, 2.0)
glVertex3f(3.0, -1.0, 2.0)
glVertex3f(4.0, 1.0, 2.0)
glColor3f(0.3, 0.9, 0.3)
glVertex3f(-4.0, 1.0, -2.0)
glVertex3f(-3.0, -1.0,-2.0)
glVertex3f(-3.0, -1.0, 2.0)
glVertex3f(-4.0, 1.0, 2.0)
glColor3f(0.3, 0.9, 0.3)
glVertex3f(4.0, 1.0, 2.0)
glVertex3f(3.0, -1.0, 2.0)
glVertex3f(3.0, -1.0, -2.0)
glVertex3f(4.0, 1.0, -2.0)
glColor3f(0.3, 0.9, 0.3)
glVertex3f(4.0, 1.0, 2.0)
glVertex3f(3.0, -1.0, 2.0)
glVertex3f(3.0, -1.0, -2.0)
glVertex3f(4.0, 1.0, -2.0)
glColor3f(0.3, 1.0, 0.2)
glVertex3f(-4.0, 1.0, -2.0)
glVertex3f(-4.0, 1.0, 2.0)
glVertex3f(4.0, 1.0, 2.0)
glVertex3f(4.0, 1.0, -2.0)
glColor3f(0.3, 1.0, 0.2)
glVertex3f(-3.0, -1.0, 2.0)
glVertex3f(-3.0, -1.0, -2.0)
glVertex3f(3.0, -1.0, -2.0)
glVertex3f(3.0, -1.0, 2.0)
glEnd()
glPopMatrix()
' turret
glPushMatrix()
glTranslatef(obj[1].tranx+obj[1].offsetx, obj[1].trany+obj[1].offsety, obj[1].tranz+obj[1].offsetz)
glRotatef(obj[1].rotx, 1.0, 0, 0)
glRotatef(obj[1].roty, 0, 1.0, 0)
glRotatef(obj[1].rotz, 0, 0, 1.0)
glBegin(GL_QUADS)
glColor3f(0.3, 0.8, 0.2)
glVertex3f(-4.0, 1.0, -2.0)
glVertex3f(-2.5, 2.0, -1.5)
glVertex3f(0.5, 2.0, -1.5)
glVertex3f(2.5, 1.0, -2.0)
glColor3f(0.3, 0.8, 0.2)
glVertex3f(2.5, 1.0, 2.0)
glVertex3f(0.5, 2.0, 1.5)
glVertex3f(-2.5, 2.0, 1.5)
glVertex3f(-4.0, 1.0, 2.0)
glColor3f(0.3, 0.9, 0.3)
glVertex3f(-4.0, 1.0, 2.0)
glVertex3f(-2.5, 2.0, 1.5)
glVertex3f(-2.5, 2.0, -1.5)
glVertex3f(-4.0, 1.0, -2.0)
glColor3f(0.3, 0.9, 0.3)
glVertex3f(0.5, 2.0, 1.5)
glVertex3f(2.5, 1.0, 2.0)
glVertex3f(2.5, 1.0, -2.0)
glVertex3f(0.5, 2.0, -1.5)
glColor3f(0.3, 1.0, 0.1)
glVertex3f(0.5, 2.0, -1.5)
glVertex3f(-2.5, 2.0, -1.5)
glVertex3f(-2.5, 2.0, 1.5)
glVertex3f(0.5, 2.0, 1.5)
glEnd()
glPopMatrix()
' Gun
glPushMatrix()
glTranslatef(obj[2].tranx+obj[2].offsetx, obj[2].trany+obj[2].offsety, obj[2].tranz+obj[2].offsetz)
glRotatef(obj[1].rotx, 1.0, 0, 0)
glRotatef(obj[1].roty, 0, 1.0, 0)
glRotatef(obj[1].rotz, 0, 0, 1.0)
glBegin(GL_QUADS);
glColor3f(0.3,0.8,0.2); ' Back Face
glVertex3f(0.0, 1.2, -0.5)
glVertex3f(0.0, 1.7, -0.5)
glVertex3f(4.0, 1.7, -0.5)
glVertex3f(4.5, 1.2, -0.5)
glColor3f(0.3,0.8,0.2); ' Front Face
glVertex3f(4.5, 1.2, 0.5)
glVertex3f(4.0, 1.7, 0.5)
glVertex3f(0.0, 1.7, 0.5)
glVertex3f(0.0, 1.2, 0.5)
glColor3f(0.3,0.7,0.3); ' Top Face
glVertex3f(4.0, 1.7, 0.5)
glVertex3f(4.0, 1.7, -0.5)
glVertex3f(0.0, 1.7, -0.5)
glVertex3f(0.0, 1.7, 0.5)
glColor3f(0.3,0.7,0.2); ' Bottom Face
glVertex3f(0.0, 1.2, 0.5)
glVertex3f(0.0, 1.2, -0.5)
glVertex3f(4.5, 1.2, -0.5)
glVertex3f(4.5, 1.2, 0.5)
glEnd()
glPopMatrix()
End Function
Function idle_function()
obj[0].roty=obj[0].roty+0.01
If obj[0].roty>360 obj[0].roty=0
obj[1].roty=obj[1].roty+0.03
If obj[1].roty>360 obj[1].roty=0
End Function
There's room for quite a few improvements with the code above, but its a certainly a good start for us.
(and before you mention it, YES.. there is a poly missing from the front of the gun turret - thats your job !) ;)