Code for 3dfx without ruin yourself - the guide for us poor people...

The plauge says:

- "If you think my english is WeIRd then you are right, english isn't my strongest side, so I hope you can stand the pain!"


Are you like me often out of money? When I first wanted to start to code 3d graphics with the 3d Blaster Voodoo2 card (It costed me a fortune only that alone!) I couldn't dream about purchasing Microsoft Visual C++, the development tool easiest to configure for Glide (3dfx own 3d graphics API), of cource there are others such as Borlands and Watcoms C/C++ compilers which is easy to use with Glide but the big problem with these compilers is that they are expensive and as you will see we can use a free C compiler system to do the same things, just requiring some modifications and tricks to be compatible with the Glide SDK. In this guide I will show you how to do this step by step but first lets talk about 3d graphics.

You not only need a compiler system to make your super graphics programs/games/demos, you need a 3d editor, a good 2d graphics program (to make the textures) and of course talent! If you are serious about 3d graphics you have most likely checked out 3d Studio MAX, a wonderful 3d graphics enviroment and I guess you have looked at the price to...
There are "cheaper" alternatives such as the wonderful Lightwave series but to expensive for me anyway.
To begin with you really need a free 3d editor, I have looked around the Internet for a free, good 3d editor to use as a tool for making the models to my 3d games, I have only found one editor that is good enough for me and if you look at the features this editor has you are most likely going to be amazed about that this program is free! It's a realtime 3d graphics modeller/renderer with texture mapping support with multiple light sources and a lot of good tools and have plugin support! One thing which is VERY good with this program is that it includes a lot of information/libraries/ headerfiles to be used by programmers which want to use it for programming applications/games/demos that uses the file format generated by the 3d editor, perfect for us 3d graphics phreaks! A important feature is that it can import models in the Lightwave format, very nice. The author tells use that in the future this program will include hardware accelaration support through Direct3d and that it will support common 3d graphics fileformats such as 3df, the format used by 3d Studio! One disappointment with it is that it has some minor bugs but they don't affects the modelling, just save your models often! The program is called Genesis 3d and can be downloaded from this site.
I just say one thing, very much thank you to the author!

Now you are probably wondering about the compiler system, don't worry, LccWin32 is a good free C compiler system that I after some work have got to work with Glide, it works with DirectX to! LccWin32 is a very good alternative for us 3d graphics programmers because the floating point performance is good and that it has support for MMX instructions in the inline assembler and directly in the C language itself! The compiler system includes many tools including resource editor, GUI programming enviroment, compiler, linker, make tool and many more. You can download LccWin32 from the LccWin32 homepage.

Ok, at last you need a 2d graphics program, you can download Paint Shop Pro(PhotoShop, Paint), a very good shareware program!

Have you downloaded and installed the programs, if you have you are propably more than ready to enter the third dimension, now follow the steps to make the LccWin32 C compiler work with Glide SDK. If you haven't downloaded and installed Glide SDK then download it from Voodooland before you proceeds. When you have downloaded Glide SDK you don't need to run the setup program, just use WinZip to extract the files. The Setup program of the Glide SDK do not recognice Voodoo2 cards so you Voodoo2 card owners shall NEVER try to do this (until 3dfx have fixed it in a later version of course).

Ok! You now have unzipped the Glide SDK, now I suggests that you downloads Acrobat Reader because the Glide SDK programmers guide/reference guide files is in two versions, one in PDF format (acrobat reader) and one in DOC format. The DOC format files doesn't work with Windows95's Write so I suggests that you downloads Acrobat Reader and use the PDF files. You can download Acrobat Reader here.

Now run the setuplcc program in the LccWin32 base directory, this program specifies where the include and library files resides. As include directory specify the include directory in the LccWin32 base directory and as library directory specify the lib directory in the LccWin32 base directory.

To make Glide to work with LccWin32 you need some tweaked include files and you can download them here.
When you have downloaded the zip file, unzip the files to a temporary directory, copy all files to the include directory in the LccWin32 base directory.
Now you shall locate your glide2x.dll file (exists in the windows\system directory if you have installed the drivers provided with your 3dfx card). Now you need to open a DOS consol. Change directory to the LccWin32 bin directory. Now execute this command line:

IMPLIB path_to_your_glide2x.dll_file

path_to_your_glide2x.dll_file are the path to your glide2x.dll file.
Now implib should have created a file named glide2x.lib. Copy this file to the lib directory of your LccWin32 base directory.

Now it's time to download a demo program to test if LccWin32 works with glide. You can download a test program here. Unzip it and you should have a *.c file (c source file).

LccWin32 has a GUI programming enviroment from which you can access everything you need to build programs so now it's time to setup the GUI enviroment.
Start the wedit program (located in the bin directory off the LccWin32 base directory). Now create a new project file, add the demo1.c source file to the project. The demo program is a Win32 consol program so remember to set the type of project to consol application.

Now you need to set some project options.
Go in under Options/Configuration/Compiler and set the include file path to the include directory of the LccWin32 base directory. Then go in under Options/Configuration/Linker and set the additional libraries to the lib directory of the LccWin32 base directory.

Now LccWin32 should be able to build and run the demo program. Choose Compiler/Generate Makefile and then Compiler/Make. After the program have been built choose Compiler/Execute demo1.exe. If the 3dfx flashscreen shows up then CONGRATULATIONS, LccWin32 now works with Glide! If it doesn't work (maybe the compiler/link/make tool complains then you must have made something wrong. I suggests that everyone reads the documentation of LccWin32, it is really easy to use when you get to know it!

That's enough for now but if you have any questions of any kind of getting this stuff to work then feel free to contact me.

Textures on the voodoo2
part #1

Is there any hot topic out there that is as hot as this?
the plague says: - "NO!!!"





So finally the voodoo2 arrived, making us stunned by the visual quality and we through away our voodoo's and prepared for the next generation of games utilizing the voodoo2 at it's maximum...

That words above could have been truth if developers had this resource before they sat down coding. You may have noticed that it isn't many games today that uses the voodoo2's texture capabilities but new upcoming titles will do it and if you want to keep you update with the coolest in the gaming industry then read on...


card.gif - 0,4 K

1. Trilinear Filtering
If you look carefully on the picture above you will notice that the Pure3d II voodoo2 card has three processors, one PixelFx and two TextureFx processors. It is the extra TextureFx processor on the voodoo2 cards that we can use to implement cool features such as trilinear filtering, a concept described in this article!

This article assumes that you have understanding of the C language, a C compiler and GLIDE SDK working with it, if you haven't check out. It is also assumed you have some knowledge of texture programming on the old voodoo, if you haven't then surf to Black Art of 3dfx Programming that have a nice tutorial on glide including texture management.

Glide docs describes trilinear filtering pretty good and if you want a reference about trilinear filtering Glide docs is the way to go, but if you (as myself) want to learn the easy way then the best way is to look at examples. Of this reason I have made a little demonstration program that shows of trilinear filtering and you can download it from the download section of this site. You will maybe be chocked over the smoothness in that demo, I was kind of chocked when I saw it! But before you downloads it you will perhaps need some basics to understand it fully.

I don't really know why trilinear filtering is infact called trilinear filtering, because it is a form of MIPMAPPING with BILINEAR FILTERING. MIPMAPPING is just a texture map with different resolutions, the different resolutions of the texture map is called MIPMAP LEVELS in the glide sdk. The largest MIPMAP level (the one with largest resolution) is used when the polygon is close to the camera and the smallest when the polygon is far away from the camera. And in the space between these two extremes the rest of the mipmap levels is used. In normal MIPMAPPING the MIPMAP is downloaded on one texture unit (texture unit is the texture processor and it's texture memory) and the right mipmap is choosed based on the distance from the viewer by the texture processor. MIPMAPPING is used to minimize the pixelization which is the result when a polygon with a texture map is zoomed in and out by the 3dengine. If you take a look at the old DOOM you will notice this is very annoying, when you come close to a wall you don't think it's a wall anymore, just a bunch of pixels...
MIPMAPPING tries to minimize this by viewing the texture in various resolutions on various distances from the viewer.

With trilinear filtering a special form of MIPMAPPING is used. A mipmap is downloaded on two different texture units and the odd mipmap levels (1,3,5,7,...) is downloaded on one texture unit and the odd ones (2,4,6,8,...) on the other texture unit. You can configure one texture unit to be used as input of the other texture unit and in the case of trilinear filtering the mipmap levels downloaded on the texture units can be combined too produce even more mipmap levels by blending one mipmap level by another. The effect is incredible smooth in/out zooming of the texture. With normal mipmapping you can see some distorsion when a mipmap level is replaced by another when the texture is zoomed in and out by the 3dengine but with triliear filtering it is as smooth as it can be (well, maybe it can be better but it is as good as it can be with todays hardware). I did mention something about that trilinear filtering is a form of mipmapping with bilinear filtering, so what is bilinear filtering?
When the texture is to be drawn at screen mapped on a polygon then bilinear filtering is used. When a texture mapped polygon is to be drawn on screen there is several ways to choose a texel from the texture map. One method chooses the texel closest to the perfect texture coordinate pair (s,t pair) to be used for rendering but bilinear filtering uses an average of the four closest pixels to the perfect coordinate. The result is a texture that holds is quality better when it is zoomed out and that doesn't show up so much pixelization when it's zoomed in. Bilinear filtering added to that special form of MIPMAPPING just described just looks fantastic if you compares it to the old tecniques used in games in the old days. To see the result of all this download the demonstration program, you will probably be amazed!

So how do you use trilinear filtering on your voodoo2 card? Well, you first need to load a mipmap from disk (if you don't know you can create a mipmap from a tga file (Paint Shop Pro can save in this format) with the texus utility which is shipped with the glide sdk, just type TEXUS -o [name of the output file] [input file]) using glide functions, and then download the even mipmap levels to one texture unit and the odd ones to the other. When this is done you need to configure the texture units to use trilinear filtering with the grTexCombine function. Exacly how all this is done is showed in the example program. You then need to set up the lightning engine on the board to make use of the computed texture rgb values by using the grColorCombine function (again, exacly how described in the sample program). The only thing that's left is to draw your polygons, just remember to snap vertices and to fill in the texture coordinates in the vertex structure. If you don't know what this vertex snapping means then take a look at Black Art of 3dfx Programming which describes this in the glide tutorial.

Happy coding and check out the part II of this article when it's finished, it will explain bump mapping and it will be uploaded along with a demo implementing the theory. Have FUN!

Textures on the voodoo2 - part#2



2. Bump Mapping




I guess you have noticed the picture above. If you have checked out 3dfx's donut demo you have seen it before. When I first saw that demo I was amazed by the bump mapping and enviroment mapping effects in that demo. If you looks carefully you will notice the bumps in the surface on the donut object. This is called bump mapping. Bump mapping is relatively easy to understand but it is some harder to do it fast.

Under construction, more info soon!


Glide3 - a topic of its own



Now when glide 3 has arrived what can we expect of it? I have been working with glide3 for a while and I have had the pleasure of witnessed some speed improvements but most of all PROBLEMS. I have had a lot of problems with the drivers and with inconsistencies and errors in the glide3 manual. Actually it took me over two weeks to get clip coordinates to work properly...

As I already have told you it have been a lot of bugs in the glide3 drivers. Although I have problems I don't think it's very serious problems, actually when I told 3dfx about my problems they fixed them and sent me new drivers in a time of some days and my problems was solved! I think that such a speedy responce from such a big company is really great. If all software companys would act like 3dfx I think software would be a lot more bugfree.

In this article you can find some info on how too manage glide3 before you get crazy, I was very close to that I can tell you!

First of all:
In glide3 you have two kinds of coordinate systems.

- Window Coordinates
- Clip Coordinates

Window coordinates is exactly the same as glide2 coordinates. The application provides x,y and oow and optionally r,g,b and texture coordinates. In glide2 you had too maintain perspective projection of your own but in glide 3 you have the new clip coordinates.

In clip coordinates glide is doing perspective projection. This means that you need to provide x,y and w. Glide is performing perspective projection using this formula:

xp = (x/w+1)(HorResolution/2)+valuex
yp = (y/w+1)(VerResolution/2)+valuey

This means that you need to specify x in the range of -w..w and y in the range of -w..w if you want your pixel visible on the screen. I think the name clip coordinates is some confusing because glide isn't performing any clipping at all. The clipping is even worser in glide3 as the old glide util clip functions is removed. As you can't clip polygons in 2d anymore because glide is performing perspective projection we have too do real 3d clipping. The 3d clipping is quite easy to do anyway since we have a 90 degree FOV (field of view) in both x and y. This is caused of glides perspective projection formula. (It seems like the guys at 3dfx knows a great deal of 3dengines...)

I have put up a new glide3x.dll for voodoo2 card in the filearea. The reason for this is that currently the reference drivers at 3dfx homesite is buggy.

If you want to really watch glide3 in action I recommends that you takes a look at my glide3 sample. It contains some new glide3 functions such as clip coordinates and support for triangle fans and the new glide3 grDrawVertexArray function call that is using the new triangle setup engine on voodoo2 cards. It works on old voodoo's too but don't makes use of a triangle setup engine as voodoo cards doesn't have one... All this is maintained by glide3 happily.

As the new glide3 is coming I guess you lccwin32 users want your compiler to work with glide3 too. The steps for using glide3 with lccwin32 is as follows:

- Download glide3 sdk from Voodooland.

- Unzip it to some directory.

- Copy all glide3 header files (h files (glide.h glideutl.h and so on...)) to your lccwin32 include directory.

- Download the lccwin32 glide3 fix from the file section of this site.

- Unzip it to some directory.

- Copy the glide3.h, 3dfx.h and glideutl.h files included in the fix to your lccwin32 include directory.

- When you writes your glide program you need to link your program with the glide3.obj file in the glide3 fix package you downloaded.

- When you writes a program you must call the LoadProcAddresses function before you calls any glide funtion. This routine loads the glide3x.dll functions dynamically to your application. (This is done automatically in MSVC++ but in lccwin32 we needs to do this manually.) You also need to #include the glide3.h file in the source where you are calling the LoadProcAddresses function from.

- You can download a demonstration program that does all this. If you can compile and run the program you can use glide3 with lccwin32.

A last little note:
My old article about getting lccwin32 to work with glide2x have some problems. First when you do implib of glide2x.dll implib is creating glide2x.lib. The problem is that a program that is linked to that glide2x.lib file requires glide2x.dll in the directory where you did implib on. All users that is using your program must have glide2x.dll in that directory. This isn't good but I didn't know this when I wrote the article... My new method in the glide3 fix doesn't have this problem anymore as the glide3 functions is loaded dynamically from the users REAL windows system directory and that directory can vary between windows installations.


This is going to be a tutorial on how to put together a simple 3d engine. It assumes you already know a bit of Glide and MSVC++ programming but you don't have to be a diehard professional. You just need to know the basics. I'll assume you already have your Glide SDK running and you can compile some simple Glide-programs. Here is a little program to setup and close your Glide programs. The code in the tutorial was written for Glide but most of it can easily be used for any other system like OpenGL or DirectX.

We'll take a look at a couple of problems:

  1. Making an Object structure
  2. Loading an Object in to your structure using 3ds Asc files
  3. Rotating an Object
  4. Gouraud shading our Object
  5. And loading and displaying TGA files

I just want to make 1 more statement. Many of the things I write may not be very elegant code and I know that most things can be done far more efficient but I'm a starter, just like most of you. This is the way I managed to do certain this. If you have any comment on how to improve my program's...please let me know.

You can reach me at tkrul@casema.net


Making an Object structure

The Object structure is a structure in which we will keep information concerning a specific object, like shape, color, position, etc. First we should know which information we need to save in our structure. We'll need at least the Vertex en Faces information and maybe we could use some sort of object id number. We'll also need to save our 2d 'screen' coordinates. Let's split up our problem and fist take a look at how to save a single vertex. Take a good look at Structs1.txt to see that I mean.
Structs1.txt

Next we do this for every datatype we need. These can be Faces, Normals, 2d and 3d coordinates, etc. When completed I could look something like this:
Structs2.txt

Now we can simply define a Object structure. I use pointers in the object structure because we don't know the number of vertices and/or faces in advance. By using pointer we can just load a file in memory and then make the pointer to point to the information in memory. The whole file should look something like this:
Structs3.txt


Loading an Object in to your structure using 3ds Asc files

Now starts the fun part, loading you own 3d files. Let's have a look at this so called ASC fileformat. This is a sample of such a file:
AscFile.txt
Acs files can be generated using 3dstudio (if anybody knows any other piece of software which exports this format, please let me know !) but free ascfiles can be found all over the web.

As you can see the file starts with the Ambient light color. For now we can just skip this. After that follows objectname, number of vertices and number of faces. This is the first information we need to know. We will use fgets and fscans to read a line at a time. We can excess the file using the following code:

int LoadAscFile(char *filename, D3Object_ptr Object)
{

FILE *fp;
char line[80];
if ((fp=fopen(filename,"r" ))==NULL)
{

printf("Error opening file: Can't find file\n");
return(0);

}
fgets(line,80,fp);fgets(line,80,fp); // Skip the fist 3 lines and read the 4th line to be processed
fgets(line,80,fp);fgets(line,80,fp);
sscanf(line,"Tri-mesh, Vertices: %d Faces: %d", &Object->NumVertices, &Object->NumFaces);

}

Now the only thing that remains is reading our vertices and faces. But before we can do that we must make a local pointer and reserve memory for this info.

D3Vertex_ptr D3VertexList = NULL; // first make a pointer
D3VertexList = (D3Vertex_ptr) calloc(Object->NumVertices, sizeof(D3Vertex) ) // Than reserve enough memory to store the vertices;

The whole loadroutine could look something like this:

int LoadAscFile(char *filename, D3Object_ptr Object, float scale)
{

FILE *fp;
char line[80];
char trash[20];
D3Vertex_ptr D3VertexList = NULL;
Face_ptr Faces = NULL;
float x,y,z;

if ((fp=fopen(filename,"r" ))==NULL)
{

printf("Error opening file: Can't find file\n");
return(0);

}

fgets(line,80,fp);fgets(line,80,fp);
fgets(line,80,fp);fgets(line,80,fp);

sscanf(line,"Tri-mesh, Vertices: %d Faces: %d", &Object->NumVertices, &Object->NumFaces);

D3VertexList = (D3Vertex_ptr) calloc(Object->NumVertices, sizeof(D3Vertex) );
Faces = (Face_ptr) calloc(Object->NumFaces, sizeof(Face));


fgets(line,80,fp);
for (int count=0; count<Object->NumVertices; count++)
{

ReadTil("Vertex");
fgets(line,80,fp);

sscanf(line,"%s X: %f Y: %f Z: %f", &trash, &x, &y, &z);
D3VertexList[count].x=x * scale;
D3VertexList[count].y=z * scale;
D3VertexList[count].z=y * scale;

}
for (count=0; count<Object->NumFaces; count++)
{

ReadTil("Face");
fgets(line,80,fp);
sscanf(line,"%s A:%d B:%d C:%d",&trash, &Faces[count].a, &Faces[count].b, &Faces[count].c);

}
Object->Vertices_local = D3VertexList;
Object->Faces = Faces;
Object->Vertices_world = (D3Vertex_ptr) calloc(Object->NumVertices, sizeof(D3Vertex) );
Object->Vertices_screen = (grVertex_ptr) calloc(Object->NumVertices, sizeof(grVertex) );
Object->Vertices_normalStat = (Normal_ptr) calloc(Object->NumVertices, sizeof(Normal) );

PrecomputeVertexNormals(Object);

fclose(fp);
return(1);

}

Note:
- This routine does a few other things besides just loading an object in memory. It can take a scale factor as argument. This scalefactor is used to scale the object when it is loaded. This is simply done by lust multiplying all vertices with the scale factor.
- fscanf could be used is many cases which would have resulted in cleaner code, but for some reason it just didn't work ! Any ideas ?
- I use a command called PrecomputeVertexNormals(...). This may seem useless at this time as there is no such function but it is used if gouraud shading is enabled. I'll get to this function when talking about gouraud shading.
- The function ReadTil(string) reads on till the string is found. Most asc files contain pagenumbering, so if we always just go to the next line we will get in trouble. ReadTil( ) is defined like this:

#define ReadTil(string) while (strcmp(line, string)) fscanf(fp, "%s",line);


Rotating an Object

Now I'll be explaining just the minimal you'll be needing to know about 3d rotation. If your serious about coding 3d you should get yourself a book. A good startingpoint would be "The Black art of 3d Game programming". It is written for dos and uses old technique's but it's illustrates the problem's well.

If we want to rotate an object in three dimensions we first have to look how we're going rotate an object in two dimensions, as this is almost the same problem but if we want it in 3d we must do it over tree angles instead of one.
Take for example the figure shown here and we want to rotate point A to point B. The rotationangle is now C. Normally we would do something like this:
Xnew = COS( C ) * Xold
Ynew = SIN( C ) * Yold

But this only works if C is the angle calculated from the horizontal line.
If we want to work around this problem we will need to change our calculation in to:
Xnew = COS( C ) * Xold - SIN( C ) * Yold
Ynew = SIN( C ) * Xold + COS( C ) * Yold

By now we have rotated one point over one angle in one direction. The next thing to do is just repeating this principle in all direction X, Y and Z. A MSVC++ code version could look like this:

void RotateObject(D3Object_ptr Object, int angleX, int angleY, int angleZ)
{

float temp_x,temp_y,temp_z;
for (int count=0;count<Object->NumVertices;count++)
{

temp_x = c[angleX] * Object->Vertices_local[count].x - s[angleX] * Object->Vertices_local[count].y;
temp_y = s[angleX] * Object->Vertices_local[count].x + c[angleX] * Object->Vertices_local[count].y;

Object->Vertices_world[count].x = c[angleY] * temp_x - s[angleY] * Object->Vertices_local[count].z;
temp_z = s[angleY] * temp_x + c[angleY] * Object->Vertices_local[count].z;

Object->Vertices_world[count].y = c[angleZ] * temp_y - s[angleZ] * temp_z;
Object->Vertices_world[count].z = c[angleZ] * temp_z + s[angleZ] * temp_y;

}

}

As I said this is the absolute minimal you need to know if you want to create a 3d program for your self. Please refer to some good books if you want to know more.


Gouraud shading our Object

Let's see,...We now have our Object loaded, rotated and we could easily render it to screen. Just divide every X and Y by its Z (the further the point is the bigger Z, so the point is divided by a bigger value resulting in points that are closer to the origin (which should be at the center of the screen) if they are further away from the camera. But if you didn't use any color in your polygons it is going to look really awful. Let lighten this up a little bit.

Let me start with flat shading. If we flat shade an object we calculate the normal of a face, calculate the angle between the lightsource and the face normal and use this angle as an indication of the amount of light that hits this face. See the following pictures to illustrate this.

We can calculate a normal using the following formula.
Let us assume we know the vectors AB (P.x ,P.y, P.z) and AC (Q.x, Q.y, Q.z). The normalvector now has the following components.
N.x = ( ( Q.y * P.z ) - ( Q.z * P.y ) )
N.y = ( ( Q.z * P.x ) - ( Q.x * P.z ) )
N.z = ( ( Q.x * Py ) - ( Q.y * P.x ) )

To calculate P and Q use the this: Let us assume B and C are to coordinates with are not in the origin and we would want to know the vector BC. To get the components of BC just subtract all the B components from the C components. This would make
P.x = B.x - A.x
P.y = B.y - A.y ... etc ( this also counts for Q )

We now can calculate the angle C between the ligthsource and the normal of the face.
LengthOfNormal = SQRT( ( N.x * N.x ) + ( N.y * N.y ) + ( N.z * N.z ) )
LengthOfLight = SQRT( ( Light.x * Light.x ) + (Light.y * Light.y ) + ( Light.z * Light.z ) )
Angle = ACOS (( N.x * Light.x )+( N.y * Light.y )+( N.z * Light.z )) / ( LengthOfNormal * LengthOfLight )

Now use the Angle to scale all color components R,G and B of the face.
All this should result into an object looking something like shown left. All the calculation stay the same if we go on to gouraud shading. The only difference in we don't use the facenormals but we calculate vertexnormals.
Just figure out which faces share a vertex. Then calculate the normals of these faces, add them and divide them by the same number of faces.
Calculate the angle C between the Light and the Vertexnormal as we did with flat shading and again use it to scale the colorcomponents of the vertex. The picture left is an example of how it could look... not such a good one but that is because it is handdraw.

Simple..huh ?

Well, I skipped some details. One such is we must do the calculation of the vertexnormals in advance and save them in memory to decrease the number of calculation we have to do in real-time.

Here is the sourcecode for recalculating the vertexnormals, calculating the angle between the lightsource and the vertexnormal and scaling the colorcomponents.


And loading and displaying TGA files

This part of the tutorial has nothing to do with 3d programming, but if you are creating a game or any other application you will sometimes have to use static ( non 3 dimensional ) screens. These are almost always just simple pictures that fill the whole screen. I will show you how you can load a TGA picture directly on screen and how you can load an TGA in to memory and then copy it to the screen using grLfbWriteRegion().
All TGA routines I describe only work with UNCOMPRESSED TGA !

Loading a uncompressed TGA is very simple. Skip the first 18 bytes, then read all the pixelsvalues as RGB (one byte each). That means if we read a screen of 640 x 480 we need to read 640*480*3 bytes = 921600 bytes

Look here to see an examplesource


Well, that's is for now... Hope I've helped some of you out there. The full source can be downloaded here . Let me hear of your progressions and mail me if you have any questions.


GiMMiC software 1998
Tommy Krul, The Netherlands

So,... I'm back again. Got massive reaction on my first tutorial. Many thanks go to all of you who took the effort on mailen me with questions and ideas...
With this second tutorial I'll try to correct a few thing I did wrong the first time. Let me start of with apologizing for some of the errors of the sourcecode which came with the first tutorial. Thanks to all of you who attented me on some of those errors. But although I really do appriciate your comment (and keep sending them !) the code is not intended to be very good... Most of the code that I published were only the first (working) version of the procedures. I did this to make sure all of you out there do not just copy and paste my code but you start coding yourself. As you will soon discover it feels much more rewarding if you coded the biggest part yourself. To fully understand code that someone else has writting is almost impossible !
I'll also talk about somethings that in the first tutorial I assumed you all already knew like setting up the compiler. I just want to make this tutorial as complete as possible. For all of you already compiling your own glide programs, just skip that part.
Finally I must admit that the tutorials don't really contain a really good storyline. The subjects don't really follow a cronological order. I'll be doing a revision of all tutorial when I have written some more. OK, now let's go back to our subject.

In this tutorial I will discuss these problems

Setting up the compiler

I must admit that almost all sites that have something to do with programming 3dfx have a small tutorial on who to set the compiler up, but I got quite some question on this subject ( hope everything is working now Maxman ) so I'll give my version of it aswell. It is a copy of a mail I wrote for someone. Just skip this part if you already can compile your programs. I assume you are using Microsoft Visual C++, but if you are using an other compiler the procedure should be almost the same.

Be sure to have your include and library directories correctly configured. You only have to do this the first time.
To do this do the following:  
  • Go to the Tools menu and click Options. Now click the Directories tab and add the directories where MSVC can find your Glide SDK.
  • Under "Show directories for:" select "Include files" and add the directory for the includes. This should be something like    
    X:\GLIDE 3\3DFX\SDK\GLIDE3X\SRC\INCLUDE
     
  • Now select show Library files and add the library directory
    X:\GLIDE 3\3DFX\SDK\GLIDE3X\SRC\LIB\WIN32
     

Now start a new Workspace. To compile a glide program you always have to start a project. As I'm not a really good windows programmer I use Console Applications. Just open a new Workspace and start a console application. After MSVC++ has made some files and directories, go to the PROJECT menu and click SETTINGS. Now do the following  

  • Go to the C/C++ and add __MSC__ to the Preprocessor definitions.  
    THESE ARE 2 UNDERSCORES ON EITHER SIDE !!! --> ( " _ _ MSC _ _ " ). When I started programming glide it took me 2 weeks of frustration to find this out !  
  • Set the Warning level to Level 1.
  • Now go to Code Generation (still under the C/C++ tab ) and set the Processor to Pentium and Calling convention to __stdcall.
  • Click the Link tab and add glide3x.lib to the Object/library modules.  

MSVC is now configured correctly and you should be able to compile your own glide programs.

So far for Compiler-stuf. Give me a minute to start a cd ( Buckshot Lefonque "Music Evolution" )...much better. I hate silence !
Now let us make the giant leap to loading textures. See my first tutorial for subjects concerning problems that fit between compiler setup and loading texuremaps.


Texture maps and how to load them

For all of you who don't know what texture mapping is ( but I think most of you already know ) I'll explain it in short. Texture mapping is a techniek used to greatly increase the detail of objects without the need for complex geometric transformations. These textures can be realtime or prerendered images. There are about a dozen techniques to acomplish this but wI will limit myself to the most commenly used methode, as we are dealing with glide and glide does most of the hard work for us.

Back in the old days we had to do texture mapping ourselfs.
The basic idea is take a image, transform it in some way and render it onto a triangle. Sounds easy ? Well, with glide it is...but getting it to run smoothly in poor old DOS was a nigthmare.

How to use textures in glide
Well, let me first explain how glide uses textures. Using glide textures have to be square or rectangle arrays of data. Every value in that texture is called a texel and has an address (s,t) each in the range of an 16 bit integer [ -32768...32767 ]. I'll assume you are using Window coordinates, as Clip coordinates seem to give some problems on older Voodoo cards. If we are using Window Coordinates all textures have there longest side texture coordinates running from 0 to 255. The other side of the texture has a length of 255/n where is n = [1,2,4,8]. This makes textures have an aspect ratio 1:1, 1:2, 1:4, 1:8. See the Glide 3.0 programming Guide page 86/87/88 for more details.

The vertexstructure now needs additional information. This information should tell glide the (s,t) coordinates of the specific vertex. Just add GR_PARAM_ST0 to the vertexLayout. See example 1. The (s,t) (in my code called (u,v) ! ) must be multiplyed by (1/ Z ) before passing them to glide !

Let's take a look at what more glide needs to know and how it should be setup to use textures. We will just have to do two things.

  • Reconfigure the Colorcombine Unit
  • And Point glide to the texture map address

(re)configuring the colorcombine unit
This is the simple part. If you are not using texture maps your colorcombinefunction should look something like example 2. To use texture mapping reconfigure the colorcombine unit as example 3.

Now comes the hard part. Before we can point glide to the texture map, naturally we should first have a texture map to point to ! Now we could do this in the main loop but that would result in really bad code, so let us write a subroutine which can load one or more texture in to memory.

...and how to load them

First we make an Texture map structure to hold texture map information (like dimensions, number of mipmaps, etc. ). The datatype for textureinformation is predifined in glide and called GrTexInfo. All we need now is just a variable to hold the address in the TMU (Teture Map Unit on the Voodoo card ! ) and a pointer to the texture map data. With this we can construct a very simple datatype to hold a texture map. It should look something like this:

typedef struct TextureMap_type
{

GrTexInfo TexInfo;
FxU32 VideoMemoryAddressTMU0;
void *TextureData;

} TextureMap;

Now load the texture map information from the *.3df file using gu3dfGetInfo(FileName,&TexInfo) where TexInfo is a datatype Gu3dfInfo (also predefined in glide. The amount of memory required for the mipmaps can now be read for TexInfo.mem_required. So just malloc that amount of memory at TexInfo.data. When you reserved that memory you can load the texture into TexInfo using gu3dfLoad(FileName,&TexInfo). We would have been ready now if we didn't wrote this loadroutine as a subroutine...
If you do write it as a subroutine we have to give our global structs the same values as our local structs. Also, if we want to be able to load mutiple texture we have to keep track of the first free address in the TMU. The lowest possible address can be obtained using grTexMinAddress(GR_TMU0). But this always returns the same value as it always just returns the lowest possible offset of the TMU. So lets initialise a global variable named NextTextureOffset and at the beginning og our program give it the value of grTexMinAddress(GR_TMU0). If we load a texture we now simple load that sample at this offset and when we're done we add the size of the just loaded texture to the offset... Simple ?
Take a good look at this source to get a better feeling of what I mean.

And Point glide to the texture map address
Pffff, let me take a break,....Are you still there ?...Finally I've got some good news. If you understood everything I wrote so far,...than the rest of this tutorial will seem very simple...Use the grTexSource command to point to your texture. Its first agrument is the value of the Texture map unit where the texture map is located. These can be GR_TMU0, GR_TMU1 or GR_TMU2. Then follow the startaddress (name_of_the_structure->VideoMemoryAddressTMU0), which mipmaplevels can be found at this address (..._EVEN, .._ODD, ..._BOTH ) and format and dimensions of the texture. This last one we loaded into a structure, when we loaded the texture from disk. So simply point glide to your texture using:
grTexSource(GR_TMU0,tex1->VideoMemoryAddressTMU0, GR_MIPMAPLEVELMASK_BOTH, &tex1->TexInfo);

For some more sourcecode, download the full source at the end of this turorial.
Ps. Many thanks go to Andreas Ingo for writing the Texturemap example source, which can be found at the download section. The biggest part of this code was based upon his work. Thanks !

Well if you servived this you can take on the world ! So let us get busy with some fun stuf


How do they create those really cool transparent effects

Well if you understood everything till here than these effects are really simple ! Just setup glide to use texturemaps and enable AlphaBlending. Thats all ! grAlphaBlendFunction(GR_BLEND_ONE,GR_BLEND_ONE,GR_BLEND_ONE,GR_BLEND_ZERO)... The color of the "underlaying" triangle determens the intensety and color in which the texture is seen. A totally white triangle will fully add the two image (back and foreground ) together ( 50%, 50% ). If the triangle is gray (50% of total white ) than the result would be 75 % background 25 % foreground.


Now that we know that lets kick some ass with a few lighting effect

Difficultylevel 1
Let us start we the very, very, very cool but over hyped lensflare effect. The basic idea is that when a bright ligth passes a cameralens a reflection of the diafragma can be seen. But you really don't want to render this effect real time as the fysics that come with this effect are Fu**C*InG hard ! So we'll do (just like in all game and demo's) a fake lensflare. We'll take a look at these prerenderd ( PhotoPaint ) images of lensflares and try to figure out how they move and react.

As you can see, the starburst is at the spot where the bright light is. The centers of the holos (or "the other relections you can see" ) are always on the vector pointing from the center of the light to the center of the screen. Now this isn't to hard to create... But before we begin we must create the pictures that will form the flare. This can be done in a standard paint or raytrace program. Save them as tga files and convert them using the TEXUS program. If you aren't such a good artest, just download the full source. I have included 4 flares in 3df format.

Now this time I won't be showing all sourcecode in bits and pieces, instead I'll discribe a good path to take in programming this effect. If you are really going to have big problems programming this, take a look at the full source. There you can find two routines called Render and RenderLensFlare which produce the flare.


Difficultylevel 2-3-4
Well this piece is going a bit more difficult than the lensflare. I'm going to discuss implanting a SIMPLE particle system.
Q:
What is a particle system ?
A:
It is a techniek used for simulating complex visual effects like explosions, fire, water, smoke, etc..
Q:
Go on !
A:
It works by simulation indivitual particles. A particle can be dead or alive. The movement of every particle follows a set of rules. These rules are simple rules, like 'a part accelorates towards the ground'. Every particle is given a starting position, starting direction, speed, energie, acceloration, ect. Then let when follow the rules and you have your particle system.
Q:
Mmmm,...sounds great but I don't understand a thing you're saying !
A:
Let me show you.
This is what I did for creating a really cool explosion.

The Explosion
Take lets say 100 particles and let them all share the same startingpoint. Now give them a a random direction ( x, y, z ) and speed. Be sure to not take values that are to big ! We are dealing with high framerates ! This is all just setupwork.
Now when it comes to animating the particles. If the particle is alive, just add DirectionX*Speed to PositionX, DirectionY to ....and DirectionZ to ... Multiply the Speed and Color by let us say 0.93 to scale them down. Now if the Speed of the particle comes below a certain trashhold, kill it, else render it ( use the same render routine as you wrote for the flensflare ! ). Simply do this for all particle. To easy for you ? Try this: When an explosion is at its brightest the light should blind the viewer...Try fading the screen to white and back when a big explosion occurs.

The Fire
Fire can be done in exactly the same way as an explosion. But when it comes to killing the particle, don't kill it but give it new values just as you did when setting up the particle system. Fires also don't need as many particles as an explosion to look convinsing.

Smoke
Help yourself with this one. It can be done in almost the same way as fire. Just use an other texture ( a more blurry one ) and make the particles move slower. Maybe you can take sideeffects like winddirection into account.

Note:

Well, of course there a bounch of you who for whom this is all far to simple...Certain Effects can follow each other. Try make a fire that slowly dies and starts smoking...Just let you fantasy go free !


Well, that's about it for now. Let me know that you would like my to write about on my next tutorial...Feel free to mail me with your questions and ideas. Happy New Year.



A last minute note:
To be able to compile the source you need the mozart.asc file from the first tutorial.

Download full sourcecode here.

GiMMiC Software 1999
Tommy Krul, The Netherlands

Tools for Programming and more on www.voodooland.org