Author: Cryect
Well here is a 3d Engine similar to the wolf3d engine except for one main thing, which is it doesn't
use raytracing which is fine. I make use of glut mainly so the program was faster to write. This
tutorial is meant for use with Microsoft Visual Studio, but it can be easily applied to others(or at
least I'm assuming). Oh and before you can take advantage of this tutorial you will need to download
the glut libraries off the internet. So go get them
here if you don't already have them. Next download these files from here
which contains some code you need for the program to compile and work. Alright first thing after
all that is to create a file and call it whatever you want. That was simple enough of course I hope
Alright we are going to start off with the headers we are going to use. So insert this code in
These will give us all the commands we are going to be using in the program. Now real fast tell
the program to compile. It will ask to create a default program say yes after that cancel the build
now add to the program the files 'texture.c' and 'bitmap.c'. Now goto the project settings and click
on the link tab and add to the libraries to be used opengl32.lib, glu32.lib, and glut32.lib. Okay now
that we have all that stuff out of the way we are going to create the map. The map will be 8x8 grid
of blocks in an array. If the number '0' is in a place then the spot will be filled in. If the
number is '1' its open there and if its '2', its just '1' except with a simple blood pool on the ground.
So here insert this into your code.
Now we are going to create a structure that can hold info for walls, floors, ceilings, and those pools
of blood on the floor. It will need to consist of two vectors. For the walls they stand for the two
end points and we need to make sure that we keep the vectors in the right order to clipping of the
counter clockwise walls. For the floors, ceilings, and blood pools the vectors each stand for a corner.
So here is how we are setting up this structure so insert it in also
We are also going to set up some limits to the amount of walls and everything. Its not really needed
for this map but for somethings you do its nice to do(though thats an advantage to linked list). Also
we need to declare the walls and the rest. So here is the code for the limits and the rest.
Now we need some variables for various constants. Some of those variables are for holding texture
numbers. One just gives a number for the constant we are using for Pi and the rest hold the numbers
for our position and angles.
Well now we are going to create some functions that allow us to add walls, floors, ceilings and the
blood pools. I won't explain them much since you can look over it and figure it out for yourself
just as easily as I can tell you.
Alright now we are about to get into some of the more complex stuff. We are going to setup the
functions that add the floor and ceilings to the approiate arrays. Its quite simple all we have
to do is search through the map array and for any spot that doesn't have a '0' must have a floor
and ceiling. Also if it doesn't equal '0' we should check to see if the number equals '2' and if
it does then lets add a blood pool also. So here is the code for that.
Alright that was pretty simple wasn't it! =) Okay the code for the wall adding code is a little more
complex. We need to when we are setting up the walls there is a empty space next to it but also a
filled spot. We need to set it up to go along the edges in a special way also. Here is the code
for it see if you can look over it and figure it out. One of the best ways to understand code is not
for someone to tell you how and what it does but for you to manually go through it once. Try it!
As you can see that was pretty much the same as the rest. Now for the Draw Scene routine that puts all of
the drawing stuff all together. Here is the code and I will explain how each part works as we go along in
the comments amongst the code.
Okay that wasn't too bad was it. The reason we have the graphic parts in seperate functions is to
make it easier to find bugs and to help us to determine what parts need to speed up when do profiling.
For this program you don't have to worry about bugs and there isn't too many things you can do speed
it up but there is some which are pretty easy to implement and I will explain a little at the end. But
now on to setting OpenGL. It needs to get information on we are handling things. Examples of this
are the color we want it to clear the background to and how the depth buffer is handled. I have some
fog code within this code but its commented due to it not working correctly with the microsoft drivers
but if you are using a non-microsoft video drivers it should work fine if you uncomment it(oh and if
you use mesa drivers the bloodpools will appear as a solid black for some reason).
Okay so that is function we call when we need to setup OpenGL then. Okay if we have a map maybe
there should be a way to move around with. Well with glut there is a special function that can
be specified for handling keys and here is how we are setting up the keys
Well as you can see that was real simple. Well we are almost done now and are in the last stretch.
But first plug this code in that handles the resizing.
Hard to explain that code really since there isn't much to say. Well here is the last bit of code.
The main function that is.
Okay compile that now and try it out. That should work pretty well. Ways to make this run faster
and addon to the program by yourself, figure out how to setup the walls to be combined together and
also the same for the floor. Thats not too hard. Also there are texture objects but I will tell you
about them later. Don't worry another tutorial is coming soon(now the question is how soon though =)
This site, and all content and graphics displayed
on it,
Difficulty: Medium
#include
//The Map
int Map[8][8] = {
{1,1,0,0,0,0,1,1},
{1,2,1,1,1,1,2,1},
{0,1,0,1,1,0,1,0},
{0,1,1,1,1,1,1,0},
{0,1,1,1,1,1,1,0},
{0,1,0,1,1,0,1,0},
{1,2,1,1,1,1,2,1},
{1,1,0,0,0,0,1,1}
};
//Structure For Walls, Floors and anything else
struct Wall{
float V1[2];
float V2[2];
};
#define MaxWalls 64
#define MaxFloors 64
#define MaxCeil 64
#define MaxBlood 16
int WallNum;
int FloorNum;
int CeilNum;
int BloodNum;
Wall WallList[MaxWalls];
Wall FloorList[MaxFloors];
Wall CeilList[MaxCeil];
Wall BloodList[MaxBlood];
//Texture Numbers
GLuint WallTex,Concrete,BloodPool;
// Define a constant for the value of PI
#define GL_PI 3.1415f
// Rotation amounts
static GLfloat yRot = 0.0f;
static GLfloat xPos = 256.0f;
static GLfloat yPos = 256.0f;
void AddWall(int x1, int y1,int x2,int y2)
{
if(WallNum
void SetupFloorAndCeil(void)
{
for(short x=0;x<8;x++)
for(short y=0;y<8;y++)
if(Map[x][y]!=0)
{
AddFloorCeil(y,x,y+1,x+1);
if(Map[x][y]==2)
AddBlood(y,x,y+1,x+1);
}
}
void SetupWalls(void)
{
for(int n=0;n<8;n++)
if(Map[0][n]!=0)
AddWall(n,0,n+1,0);
for(n=0;n<8;n++)
{
for(int y=1;y<8;y++)
if((Map[y][n]==0)&&(Map[y-1][n]!=0))
AddWall(n+1,y,n,y);
}
for(n=0;n<8;n++)
if(Map[7][n]!=0)
AddWall(n+1,8,n,8);
for(n=0;n<8;n++)
{
for(int y=0;y<7;y++)
if((Map[y][n]==0)&&(Map[y+1][n]!=0))
AddWall(n,y+1,n+1,y+1);
}
for(n=0;n<8;n++)
if(Map[n][0]!=0)
AddWall(0,n+1,0,n);
for(n=0;n<8;n++)
{
for(int y=1;y<8;y++)
if((Map[n][y]==0)&&(Map[n][y-1]!=0))
AddWall(y,n,y,n+1);
}
for(n=0;n<8;n++)
if(Map[n][7]!=0)
AddWall(8,n,8,n+1);
for(n=0;n<8;n++)
{
for(int y=0;y<7;y++)
if((Map[n][y]==0)&&(Map[n][y+1]!=0))
AddWall(y+1,n+1,y+1,n);
}
}
Okay hopefully you understand that all. Was a little confusing for me myself and it took a while
to get it just right since sometimes I was facing walls the wrong direction so unless you were inside
of a section you wouldn't see it. Now drawing these walls is a little easier since all we have to
do is go through the array of walls and draw them all. Here is the code for that and I will explain
all the opengl functions then that are used there.
void DrawWalls(void)
{
//Here is where it goes through drawing all the walls
glBegin(GL_TRIANGLES);
for(int n=0;n
Alright see the command glBegin, where we pass the argument GL_TRIANGLES, that tells opengl we are
about to draw a bunch of triangles. Every time we have given it 3 vertices it will draw a triangle.
To give it a vertex we use the command glVertex3f which accepts 3 floats with the first one signifying
the x-axis, then the y-axis and finally the z-axis. But before that we call the command glTexCoord2f
which tells OpenGL the texture coordinates for the next point. Normally you have it between 0 and 1 but
if you have it out of the range of that numbers it will repeat the texture or clamp the texture it
based on what the texture is set to. The command glEnd tells OpenGl we are done with drawing our
triangles. You need to make sure do this between changing textures otherwise you won't change the
texture and not know why. Okay here is the code for drawing the rest of the things its pretty much
the same except for they are all horizontal instead of vertical.
void DrawFloors(void)
{
//Here is where it goes through drawing all the floors
glBegin(GL_TRIANGLES);
for(int n=0;n
// Called to draw scene
void RenderScene(void)
{
// Clear the window with current clearing color and clears our depth buffer
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
// Save the current matrix state(otherwards our position and how we are looking)
glPushMatrix();
//Rotate according to our angle
glRotatef(yRot, 0.0f, 1.0f, 0.0);
//Move us to the correct corresponding spot
glTranslatef(-xPos,0.0f,-yPos);
//Enable the use of 2d Textures
glEnable(GL_TEXTURE_2D);
//Tell opengl to use the wall texture
glBindTexture(GL_TEXTURE_2D,WallTex);
//Draw the Walls now
DrawWalls();
//Now tell OpenGL we want to use the concrete texture which is used for the floor and ceiling
glBindTexture(GL_TEXTURE_2D,Concrete);
//Draw the floor and the ceiling
DrawFloors();
DrawCeils();
//Set the texture to the Blood Pool
glBindTexture(GL_TEXTURE_2D,BloodPool);
//Enable Blending so we are basiclly multitexturing(except I'm not using the stuff so it really is)
glEnable(GL_BLEND);
//This multiplies the source color by the destination color
glBlendFunc(GL_DST_COLOR,GL_ZERO);
//Draw The Blood Pools
DrawBloodPools();
//Disable Blending
glDisable(GL_BLEND);
// Restore transformations
glPopMatrix();
// Flush drawing commands
glFlush();
//Swap to the other Buffer since we are double buffering
glutSwapBuffers();
}
void SetupRC()
{
//Fog
//GLfloat fog_color[4]={0.0f,0.0f,0.0f,1.0f};
// Black background
glClearColor(0.0f, 0.0f, 0.0f, 1.0f );
//Set Drawing Color to White
glColor4f(1.0f,1.0f,1.0f,1.0f);
//Cull Polygons that aren't forward(counterclockwise)
glFrontFace(GL_CW);
//Enable Culling of backward facing polys
glEnable(GL_CULL_FACE);
//Enable Depth Testing
glEnable(GL_DEPTH_TEST);
//Set it to draw if Less Than Equal
glDepthFunc(GL_LEQUAL);
//Load Texture Maps
WallTex=TextureLoad("brickwall.bmp",0,GL_LINEAR_MIPMAP_LINEAR,GL_LINEAR,GL_REPEAT);
Concrete=TextureLoad("concrete.bmp",0,GL_LINEAR_MIPMAP_LINEAR,GL_LINEAR,GL_REPEAT);
BloodPool=TextureLoad("bloodpool.bmp",0,GL_LINEAR_MIPMAP_LINEAR,GL_LINEAR,GL_REPEAT);
//Fog Setup
/* glEnable(GL_FOG);
glFogi(GL_FOG_MODE,GL_EXP);
glFogf(GL_FOG_DENSITY,0.0025);
glFogfv(GL_FOG_COLOR,fog_color);*/
}
Up Arrow - Move Forward
Down Arrow - Move Backwards
Left Arrow - Turn Left
Right Arrow - Turn Right
Now here is the code to handle what we want.
void SpecialKeys(int key, int x, int y)
{
//Turn Left
if(key == GLUT_KEY_LEFT)
yRot -= 5.0f;
//Turn Right
if(key == GLUT_KEY_RIGHT)
yRot += 5.0f;
//Go Forward
if(key == GLUT_KEY_UP)
{
yPos+=7.5*sin((yRot-90)/180.0f*GL_PI);
xPos+=7.5*cos((yRot-90)/180.0f*GL_PI);
}
//Go Backwards
if(key == GLUT_KEY_DOWN)
{
yPos-=5.5*sin((yRot-90)/180.0f*GL_PI);
xPos-=5.5*cos((yRot-90)/180.0f*GL_PI);
}
//Check the angle
if(yRot > 356.0f)
yRot = 0.0f;
else if(yRot < -1.0f)
yRot = 355.0f;
// Refresh the Window
glutPostRedisplay();
}
void ChangeSize(int w, int 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();
gluPerspective(90,float(w)/float(h),1.0,1024.0);
// Reset Model view matrix stack
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
int main(int argc, char* argv[])
{
//Setup the Walls
SetupWalls();
//Setup the floor, ceiling, and blood pools
SetupFloorAndCeil();
//Tell glut to initiate with the arguments
glutInit(&argc, argv);
//Tell glut we need a double buffer, to be able to have red, green and blue and also we need a depth buffer
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
//Tell it to create the window and name it
glutCreateWindow("QDevels Wolf3d Style Engine");
//Specify the function for whenever the window resizes
glutReshapeFunc(ChangeSize);
//Here is where tell the function for the key
glutSpecialFunc(SpecialKeys);
//And the must important function GLUT needs to know about and thats the rendering function
glutDisplayFunc(RenderScene);
//Setup OpenGl now with the rendering context
SetupRC();
//Initialize glut's main loop
glutMainLoop();
//Return back to whatever we were doing before hand
return 0;
}
are ©opyrighted to the Quake DeveLS team.
All rights received.
Got a suggestion? Comment? Question? Hate mail? Send it to us!
Oh yeah, this site is best viewed in 16 Bit or higher, with the resolution on 800*600.
Thanks to Planet Quake for there great help and support with hosting.
Best viewed with Netscape 4 or IE 3