Basic OpenGL for the Newbie, Part 1

BlitzMax Forums/BlitzMax Tutorials/Basic OpenGL for the Newbie, Part 1

Linked Lists

Wouldn'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 !) ;)