# The QB Times

## 3D in QB

Hi folks! It's me, your all-nutcase, 100% freaked out programmer, BlackBird. Nightwolf asked me to write an article about 3D techniques for his online magazine, the QB Times, and that's what you're reading right now. I originally shoud have written this for issue #1 but, as always, I was late. Nevertheless, it's here now, and it will be better then ever! (er... that shouldn't be too hard for a first time I guess)

Since I have been working on a 3D modeller (or editor, or drawing program, whatever), called 3DES Designer (or 3DES for short) for quite some time now, I figured it would be nice to combine the 3D tutorial with a 3DES tutorial, also because I have since received many emails regaring the use of the models created in 3DES. Soon I will release a 3DES SDK (or Software Development Kit) for use with your own programs.

As you might have noticed, the 3DES zipfile came with a small program called LOAD3DP.BAS which explained the 3DP file format that 3DES uses to store it's models. However, this is not sufficent for most users and therefore I will try to explain a little about the 3DES models in this article.

But first you'll have to understand the basics of 3D graphics, so here we go... Firstly, three dimensional objects consist of points in a 3D space, and for the totally braindead among us, three dimensional points have three coordinates: the X, Y and Z coordinates.

In maths classes at school (at least mine) they teach you Z is the horizontal axis and Y is the axis that points into the depth... bummer for them, 'cause I hereby treat Z as the 'depth' axis, and X and Y as the 2D axis that lie on your computer screen... let's use a nice ASCII drawing to support that:
(just imagine that the drawing is 3D and that the Z-axis points into the depth)

``` o--------> x-axis
|\
| \
|  \ z-axis
|
\/  y-axis
```
Mind that 3D coordinates are notated in the following order: X,Y,Z
Also, the arrows in the drawing above point to the positive values relative to the center of the cooridinate system (being o in the drawing), eg: negative Z values lie in front of the center, positive Z values behind it.
Got that? good. Now how do you display those points? Well, since your screen is only 2D, you can't just plot a pixel at, say 10,4,12... (or PSET (10,4,12),15) you could try... but it definitively won't work.
So what you need to do is 'convert' the 3D coordinates to 2D ones that fit onto your screen (this process is called translation).
But how? It's quite simple, just follow this formula:
```FlatX = X * 256 / Z
FlatY = Y * 256 / Z
```
Where FlatX and FlatY are the 2D coordinates that you can plot to the screen right away. Noticed the number 256 in there? Right, now that is the value that controls one of the most important things in the world of 3D graphics, perspective (meaning things get smaller the further they are away from the viewer). The larger the value, the less perspective in your object, keep that in mind. 256 is a pretty good 'default' value. You don't need to change it unless your model's perspective looks exaggerated or something.

Okay, so we've gotten quite far now. You now ought to know how to draw a 3D object consisting of points on a 2D screen...
if that is still unclear, let me clarify it for you, here's a little source code to get you started:

```-------------8<-------------8<-------------8<-------------8<-------------

'// Ajust this value to load larger models or save memory
CONST numberOfPoints = 100
'// Ajust this value to zoom in or out on the object
CONST zoomLevel = 0

TYPE ThreeDeePoint
X AS SINGLE
Y AS SINGLE
Z AS SINGLE
END TYPE

'// Set up an array of 3D points
DIM MyArray(1 TO numberOfPoints) AS ThreeDeePoint

'// Load a model into the array

'// Draw all the points
FOR pointIdx = 1 TO numberOfPoints
'// Do not draw points that are behind the screen
IF MyArray(pointIdx).Z + zoomLevel > 0 THEN
'// Translate points
flatX = MyArray(pointIdx).X * 256 / (MyArray(pointIdx).Z + zoomLevel)
flatY = MyArray(pointIdx).Y * 256 / (MyArray(pointIdx).Z + zoomLevel)
'// Plot a white dot at the point's translated coordinates
PSET (flatX, flatX), 15
END IF
NEXT

-------------8<-------------8<-------------8<-------------8<-------------
```
And voila! your model is here! Offcourse it still looks like shit, since you only used dots to draw the points.
Most 3D games and applications, like 3DES for example, use face-based models. A face consist of 3 or more points, and when linked together, they form a polygon. Here's another one of my beautiful ASCII drawings to clarify that:
```
o - point 1
/ \
/   \
point 4 - o     \
\     \
point 3 - o-----o - point 2
```
The drawing above is a face consisting of four points, to draw it, just translate all four points to 2D, draw a line from point 1 to 2, 2 to 3, 3 to 4 and from 4 back to 1, and hey presto! A face!
Offcourse it's still a wireframe representation, not a solid face, but that's yet another chapter, drawing a filled polygon, and I'm not going to explain that here.

So, with a little creativity, you can apply the face-technique to the sourcecode above, making it a so-called wireframe 3D engine. Hurray!
Still, we are missing the main part of a true 3D engine... manipulation of the model. It's very nice and all, a still of a 3D model, but it's much more fun when the thing actually rotates around it's axis.
3D rotation is basically not very different from 2D rotation, not to say exactly the same.
The only thing you need to do is rotate each point three times, one time for each axis (X,Y and Z).
Rotating a 2D point can be accomplished by using this formula:

```newX = COS(degrees) * X - SIN(degrees) * Y
newY = SIN(degrees) * X + COS(degrees) * Y
```
To translate a 3D point, you'll have to treat the rotation around each axis (3 in total) as a 2D rotation using the remaining two coordinates.
For example, to rotate around the X-axis, treat the Y,Z coordinates as if they were 2D coordinates in 2D space.

Assuming you put the above rotation function in a SUBroutine defined like this: SUB rotatePoint (X, Y, newX, newY, degrees), the correct code to rotate a point around the axis would be:

```-------------8<-------------8<-------------8<-------------8<-------------

'// around X-axis
CALL rotatePoint (Z, Y, newZ, newY, degrees)

'// around Y-axis
CALL rotatePoint (X, Z, newX, newZ, degrees)

'// around Z-axis
CALL rotatePoint (Y, X, newY, newX, degrees)

-------------8<-------------8<-------------8<-------------8<-------------
```
Then all you have to do is just rotate all points like in the example above, then translate them and then draw the bloody thing =)

Well folks, that is the end of part 1, this series will be continued in future issues of the QB Times, see yers!