Vol. 2 Iss. 4 - October 2001

A Basic 3D Engine

By Sane <sane@telia.com>

Finally, here's the 4th part in the graphics coding series, and this time we'll be making the base for a 3D engine, that we'll be building in a couple of articles from now on.

A lot of you probably know this already, but I'm gonna write it anyways in case someone doesn't.

3D (3 dimensional) means that there are 3 dimensions: width, height and depth. A normal computer screen has only got width and height, but when making 3D on a computer, you simulate a third dimension, that goes into the screen (depth), and since X is horizontal position, and Y is vertical, Z is used when referring to how far away something is (whatever that should be called).

First I'm gonna explain how we're gonna store the 3D scene for the engine. The method I like to use for storing a 3D scene is to have an array where the vertices (the points in 3D space), are stored, and another one where the polygons are stored, so that instead of storing the x, y and z values with the poly, the poly refers to vertices, and thus more than one poly can make use of the same vertex. This saves memory, makes it easier to avoid gaps between polys, and also lets you avoid calculating the position for the same point more than once per frame, in case several polys make use of the same point, which is true for approximately 99.9% of all 3D scenes with more than 3 polys :)

Here's the code we'll use for initializing the arrays:

TYPE VertexType X AS SINGLE Y AS SINGLE Z AS SINGLE END TYPE DIM SHARED Vertex(0 TO NumOfVertices) AS VertexType DIM SHARED Poly(0 TO NumOfPolys, 2) AS INTEGER

The basic rule of how to do 3D on a 2D computer screen, is that objects that are twice as far away look half as big, and to achieve that effect, you divide X and Y by Z. Also, since the viewer is most often not pressing his/her face to the computer screen (as far as I know...), the formulas should involve the distance the viewer has from the screen. I usually use something like 300, which gives pretty good results.

I don't remember why exactly, but if you don't use field of view*x instead of just x, you get strange results, so we'll do that :) The field of view is also best around 300. The last thing the formulas should do is to make a point with the (X,Y) position 0,0 go to the center of the screen, cause otherwise it would look wierd, since the center of the viewpoint would be the top-left corner. The center of a screen in VGA mode 13h (SCREEN 13) is 160,100, so that's what we'll add to the calculated screen coordinates.

And here are the formulas we get:

ScreenX=FOV*X/(Distance-Z)+160 ScreenY=FOV*Y/(Distance-Z)+100

This is all we need to know to make the first version of the engine, so here's the code for the engine:

'Made by Sane at the 9th of November 2001, for QBCM 'Set amount of vertices and polys CONST NumOfVertices = 50 CONST NumOfPolys = 10 'Variable initializing TYPE VertexType X AS SINGLE Y AS SINGLE Z AS SINGLE END TYPE DIM SHARED Vertex(0 TO NumOfVertices) AS VertexType DIM SHARED Poly(0 TO NumOfPolys, 2) AS INTEGER 'Creating a random scene FOR i = 0 TO NumOfVertices Vertex(i).X = INT(RND * 200) - 50 Vertex(i).Y = INT(RND * 200) - 100 Vertex(i).Z = INT(RND * 200) - 100 NEXT i FOR i = 0 TO NumOfPolys FOR j = 0 TO 2 Poly(i, j) = INT(RND * NumOfVertices) NEXT j NEXT i 'Set screen mode SCREEN 13 FOR i = 0 TO 100 'A simple animation thing FOR j = 0 TO NumOfVertices Vertex(j).X = Vertex(j).X - 1 NEXT j 'Redraw, wait for retrace and clear the screen DrawScene WAIT &H3DA, 8 CLS NEXT i SUB DrawScene FOR p = 0 TO NumOfPolys X1 = Vertex(Poly(p, 0)).X X2 = Vertex(Poly(p, 1)).X X3 = Vertex(Poly(p, 2)).X Y1 = Vertex(Poly(p, 0)).Y Y2 = Vertex(Poly(p, 1)).Y Y3 = Vertex(Poly(p, 2)).Y Z1 = Vertex(Poly(p, 0)).Z Z2 = Vertex(Poly(p, 1)).Z Z3 = Vertex(Poly(p, 2)).Z SX1 = 256 * (X1) / (256 - Z1) + 160 SX2 = 256 * (X2) / (256 - Z2) + 160 SX3 = 256 * (X3) / (256 - Z3) + 160 SY1 = 256 * (Y1) / (256 - Z1) + 100 SY2 = 256 * (Y2) / (256 - Z2) + 100 SY3 = 256 * (Y3) / (256 - Z3) + 100 LINE (SX1, SY1)-(SX2, SY2) LINE (SX2, SY2)-(SX3, SY3) LINE (SX3, SY3)-(SX1, SY1) NEXT p END SUB

It's quite flickery, but the point with it was only to make a basic 3d engine that works, which was achieved :) As usual, the code is available with the downloadable version of this issue, as 3DENGINE.BAS.

Next article will probably be about 3D rotations, see ya then.

-Sane